Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read-write operations #27

Closed
lveillard opened this issue May 14, 2023 · 2 comments
Closed

Read-write operations #27

lveillard opened this issue May 14, 2023 · 2 comments

Comments

@lveillard
Copy link

lveillard commented May 14, 2023

Hello! Noob question here as i'm using your project to learn rust.

Is there a way to read and write in the same transaction in order to do some conditional .put() ?

For instance adding one (key,value) only if the value is not present already

Example:
I'm opening a tx in the root,

then sending the buckets to some functions as references and inside that function i'm checking if a key exist, and if it exist, i'm throwing an error , and if not, i'm doing a .put()

 match schema_index_kinds_bucket.get(kindName) {
        Some(data) => match data {
            Data::Bucket(_) => Err(Error::KindExists),
            Data::KeyValue(_) => Err(Error::KindExists),
        },
        None => {
            schema_kinds_bucket
                .put(kindId.to_be_bytes(), kindName)
                .map_err(Error::Database)?;
            schema_index_kinds_bucket
                .put(kindName, kindId.to_be_bytes())
                .map_err(Error::Database)?;
            Ok(())
        }
    }

The error I get, which makes sense if this is not compatible with read-write, is that the "schema_kinds_bucket" may not live long enough

@pjtatlow
Copy link
Owner

This is definitely something that's supported! Without seeing more code, I'm not 100% sure what's going wrong, but here's a little test I wrote based on your code that seems to work fine.

#[test]
fn test() -> Result<(), Error> {
    let random_file = common::RandomFile::new();

    let db = OpenOptions::new()
        .strict_mode(true)
        .open(&random_file.path)?;

    let tx = db.tx(true)?;
    let schema_index_kinds_bucket = tx.get_or_create_bucket("schema-index-kinds")?;
    let schema_kinds_bucket = tx.get_or_create_bucket("schema-kinds")?;
    let kindName = "kindName";
    let kindId = 123_u8;

    match schema_index_kinds_bucket.get(kindName) {
        Some(_) => return Err(Error::IncompatibleValue),
        None => {
            schema_kinds_bucket.put(kindId.to_be_bytes(), kindName)?;
            schema_index_kinds_bucket.put(kindName, kindId.to_be_bytes())?;
        }
    }
    Ok(())
}

@lveillard
Copy link
Author

lveillard commented May 18, 2023

I was messing with borrows and other rust concepts but it definitively works.

Thanks for the example as it helped me to enhance some of my patterns :) Let me close the issue.

Btw, I did some benchmarking and is crazy how fast this goes!

The big limitations:

  1. One single transaction with a million keys write is really fast, but jammDB is less adapted to situations where the transactions are batched. The differ

For instance, for hundred keys in a slow laptop:

[basic]:write_batch_one_key_per_tx
time:   [162.69 ms 165.52 ms 168.72 ms]

[basic]:write_batch_single_tx 
time:   [3.0183 ms 3.2803 ms 3.5590 ms]
  1. No concurrent writes.

On the other hand buckets are really performant to store edges where the predicate is the name of the bucket. The difference with other KVs forced me to check my code like 100 times looking for a mistake 😅 but it seem just to be way more performant!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants