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

Is it possible to create a generic wrapper struct ? #268

Open
lazybilly opened this issue Aug 5, 2024 · 4 comments
Open

Is it possible to create a generic wrapper struct ? #268

lazybilly opened this issue Aug 5, 2024 · 4 comments

Comments

@lazybilly
Copy link

Hi !

Thank you for this library, it looks great but I'm no Rust expert and I really struggle its lifetimes and creating a struct wrapper around it.

Is it actually possible to have a generic struct owning the env, wtxn and database ?

Like so:

pub struct Database<'p, Key, Value>
where
    Key: Ord + Clone + Debug + ?Sized + 'static,
    Value: PartialEq + 'static,
{
    env: Env,
    wtxn: RwTxn<'p>,
    db: Database<Key, Value>
}

The use case is that I have a lot of little databases with a ton of data spread among them and having to manage all env/wtxn is quite complicated and kind of messy (again I'm not a rust expert). It would really be much nicer in a nice wrapper which is possible with many other databases.

@lazybilly lazybilly changed the title How to use inside a struct Is it possible to create a generic wrapper struct ? Aug 5, 2024
@Kerollmops
Copy link
Member

Hey @lazybilly 👋

Thanks for using heed and reporting possible improvements.

We recently released the Env::static_read_txn for this specific purpose but it only works with read transaction. Can you maybe propose a PR to implement that for write transactions?

@lazybilly
Copy link
Author

Hi @Kerollmops,

I'll try but I fear I might be a little too new to Rust for that.

I did actually see this static_read_txn but I couldn't figure out how to open a database with it as I sadly couldn't find any info in the docs/issues/pr/examples

let env = unsafe { EnvOpenOptions::new().open(format!("{folder}/{file}"))? }; // Create env
let txn = env.static_read_txn().unwrap(); // static read txn takes env
let db = env.open_database(&txn, None)?.unwrap(); // Can't work

env.static_read_txn eats env but then I env cannot be used to open the database, I must be missing something very obvious here !

@lazybilly
Copy link
Author

Okay figured it out thanks to zed's codebase ! Very simple but didn't feel intuitive at all at first

    let env = unsafe { EnvOpenOptions::new().open("./db").unwrap() };

    let db = {
        // we will open the default unnamed database
        let mut wtxn = env.write_txn().unwrap();
        let db: Database<Str, U32<NativeEndian>> = env.create_database(&mut wtxn, None).unwrap();

        // opening a write transaction
        db.put(&mut wtxn, "seven", &7).unwrap();
        db.put(&mut wtxn, "zero", &0).unwrap();
        db.put(&mut wtxn, "five", &5).unwrap();
        db.put(&mut wtxn, "three", &3).unwrap();
        wtxn.commit().unwrap();

        db
    };

    let rtxn = env.clone().static_read_txn().unwrap();

    // Now we can use `rtxn` to read in threads etc

@lazybilly
Copy link
Author

lazybilly commented Aug 6, 2024

Though some examples would really be appreciated, I'm having quite a hard time with lifetimes... when trying to add functions like get/iter/.. to the wrapper. It seems that I must add 'static everywhere, does it make sense ?

Here's what I got as of right now:

pub struct Database<Key, Value>
where
    Key: Ord + Clone + Debug + ?Sized,
    Value: PartialEq,
{
    env: Env,
    txn: RoTxn<'static>,
    db: Database<Key, Value>,
}

impl<'p, Key, Value> Database<Key, Value>
where
    Key: Ord
        + Clone
        + Debug
        + ?Sized
        + 'static
        + BytesEncode<'static, EItem = Key>
        + BytesDecode<'static, DItem = &'static Key>,
    Value: PartialEq + 'static + BytesDecode<'static, DItem = &'static Value>,
{
    pub fn open(path: &Path) -> color_eyre::Result<Self> {
        fs::create_dir_all(path);

        let env = unsafe { EnvOpenOptions::new().open(path)? };

        let env = env.clone();

        let mut txn = env.write_txn()?;

        let db = env.create_database(&mut txn, None)?;

        txn.commit()?;

        let txn = env.clone().static_read_txn().unwrap();

        Ok(Self {
            env,
            txn,
            db,
        })
    }

    pub fn iter<F>(&'static self, callback: &mut F)
    where
        F: FnMut((&Key, &Value)),
    {
        self.db
            .iter(&self.txn)
            .unwrap()
            .map(|res| res.unwrap())
            .for_each(callback);
    }

    pub fn get(&'static self, key: &'static Key) -> Option<&Value> {
        self.db.get(&self.txn, key).unwrap()
    }
}

As you can see, there is 'static which really doesn't feel right

@lazybilly lazybilly reopened this Aug 6, 2024
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