sdb
a user-friendly wrapper for sanakirja database
about
sanakirja author is Pierre-Étienne Meunier .
see Sanakirja 1.0 (pure Rust transactional on-disk key-value store) released!
Sanakirja is at least 10 times faster than Sled in my (sequential) benchmarks, and even 20%-50% faster than LMDB (the fastest C equivalent) in the same benchmarks. Also, I started it when there was no real alternative (Sled didn't exist at the time).
I wrapper it for easy use .
First step : static define db , see tests/db.rs
use sdb::{direct_repr, Db, DbU, Storable, Tx, UnsizedStorable};
use static_init::dynamic;
use std::env;
use std::path::Path;
#[dynamic]
pub static DIR: String = env::current_exe()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.display()
.to_string();
#[dynamic]
pub static TX: Tx = {
let dir = Path::new(&*DIR).join("db");
println!("DATABASE DIR {}", dir.display().to_string());
Tx::new(
&dir,
&[
],
)
};
#[dynamic]
pub static DB0: Db<'static, u64, u64> = TX.db(0);
#[derive(Default, Eq, PartialEq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
pub struct Hash(pub [u8; 2]);
direct_repr!(Hash);
#[dynamic]
pub static DB1: Db<'static, u64, Hash> = TX.db(1);
#[dynamic] pub static DB2: DbU<'static, u64, [u8]> = TX.db(2);
#[dynamic]
pub static DB3: DbU<'static, [u8], [u8]> = TX.db(3);
Second step : use it , see tests/main.rs
mod db;
use anyhow::Result;
use db::{Hash, DB0, DB1, DB2, DB3, TX};
#[test]
fn main() -> Result<()> {
println!("direct put");
DB0.put(&0, &0)?;
DB0.put(&0, &1)?;
DB0.put(&6, &1)?;
DB0.put(&6, &2)?;
DB0.put(&6, &3)?;
println!("- print val where key = 6");
for entry in DB0.key_iter(&6)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
DB0.rm1(&6, None)?;
{
println!("# write transaction");
let tx = TX.w()?;
let mut db0 = tx.db(&DB0);
db0.put(&1, &5)?;
db0.put(&1, &3)?;
db0.put(&2, &2)?;
db0.put(&2, &1)?;
db0.put(&3, &7)?;
db0.put(&3, &9)?;
println!("- print all key db0");
for entry in db0.iter(None, None)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
println!("- print db1 where key is 2");
for entry in db0.key_iter(&2)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
println!("- delete key 2 the first value : {}", db0.rm1(&2, None)?);
println!("- delete key=2 and value=5 : {}", db0.rm1(&2, &5)?);
println!("- delete key 3 all value : delete number = {}", db0.rm(&3)?);
println!("- delete key 5 the first value : {}", db0.rm1(&5, None)?);
println!("- print all key");
for entry in db0.iter(None, None)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
let mut db1 = tx.db(&DB1);
db1.put(&1, &Hash([1, 2]))?;
println!("- print all key db1");
for entry in db1.iter(None, None)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
let mut db2 = tx.db(&DB2);
db2.put(&1, &[1, 2, 3][..])?;
db2.put(&2, &[4, 6][..])?;
println!("- print all key db2");
for entry in db2.iter(None, None)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
let mut db3 = tx.db(&DB3);
db3.put(&[1, 2][..], &[1, 2, 3][..])?;
println!("- print all key db3");
for entry in db3.iter(None, None)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
}
{
println!("# read transaction");
let tx = TX.r()?; let db0 = tx.db(&DB0);
println!("- exist key 2 value 1 > {:?}", db0.exist(&2, &1)?);
for i in [1, 2, 5] {
println!("- get key {} > {:?}", i, db0.one(&i)?);
}
println!("- print all key");
for entry in db0.iter(None, None)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
println!("- print key greater or equal 2");
for entry in db0.iter(&2, None)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
println!("- print key greater or equal 2 and value greater or equal 1");
for entry in db0.iter(&2, &1)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
println!("- print key greater or equal 2 and value greater or equal 1");
for entry in db0.iter(&2, &1)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v)
}
println!("- print key in revese order");
for entry in db0.riter(None, None)? {
let (k, v) = entry?;
println!("> {:?} {:?}", k, v);
}
}
Ok(())
}
db method you can see src/dbpage.rs
use crate::iter::KeyIter;
pub use sanakirja::btree::page::Page;
use sanakirja::btree::{BTreeMutPage, BTreePage, Iter, RevIter};
use sanakirja::{Env, Error, LoadPage, MutTxn, Storable, Txn};
use std::marker::PhantomData;
pub type MutTxnEnv<'a> = MutTxn<&'a Env, ()>;
pub type TxnEnv<'a> = Txn<&'a Env>;
pub struct Tx {
pub(crate) env: Env,
}
macro_rules! db_page_r {
($self:ident, $db:ident, $fn:expr) => {{
let tx = $self.tx.r()?;
let $db = tx.db($self);
$fn
}};
}
macro_rules! db_page_w {
($self:ident, $db:ident, $fn:expr) => {{
let tx = $self.tx.w()?;
let mut $db = tx.db($self);
$fn
}};
}
impl<
'a,
K: ?Sized + Storable + PartialEq,
V: ?Sized + Storable + PartialEq,
P: BTreeMutPage<K, V> + BTreePage<K, V>,
> DbPage<'a, K, V, P>
{
pub fn put<IntoK: Into<&'a K>, IntoV: Into<&'a V>>(
&self,
k: IntoK,
v: IntoV,
) -> Result<bool, Error> {
db_page_w!(self, db, db.put(k.into(), v.into()))
}
pub fn rm<IntoK: Into<&'a K>>(&self, k: IntoK) -> Result<usize, Error> {
db_page_w!(self, db, db.rm(k.into()))
}
pub fn one<IntoK: Into<&'a K>>(
&self,
k: IntoK,
) -> Result<Option<&'a V>, <TxnEnv as LoadPage>::Error> {
db_page_r!(self, db, db.one(k.into()))
}
pub fn exist<IntoK: Into<&'a K>, IntoV: Into<&'a V>>(
&self,
k: IntoK,
v: IntoV,
) -> Result<bool, <TxnEnv as LoadPage>::Error> {
db_page_r!(self, db, db.exist(k.into(), v.into()))
}
pub fn rm1<IntoK: Into<&'a K>, IntoV: Into<Option<&'a V>>>(
&self,
k: IntoK,
v: IntoV,
) -> Result<bool, Error> {
db_page_w!(self, db, db.rm1(k.into(), v.into()))
}
pub fn key_iter<IntoK: Into<&'a K>>(
&self,
k: IntoK,
) -> Result<KeyIter<TxnEnv, K, V, P>, <TxnEnv as LoadPage>::Error> {
db_page_r!(self, db, db.key_iter(k.into()))
}
pub fn iter<OptionK: Into<Option<&'a K>>, OptionV: Into<Option<&'a V>>>(
&self,
k: OptionK,
v: OptionV,
) -> Result<Iter<TxnEnv, K, V, P>, <TxnEnv as LoadPage>::Error> {
db_page_r!(self, db, db.iter(k.into(), v.into()))
}
pub fn riter<OptionK: Into<Option<&'a K>>, OptionV: Into<Option<&'a V>>>(
&self,
k: OptionK,
v: OptionV,
) -> Result<RevIter<TxnEnv, K, V, P>, <TxnEnv as LoadPage>::Error> {
db_page_r!(self, db, db.riter(k.into(), v.into()))
}
}
pub struct DbPage<
'a,
K: ?Sized + Storable + PartialEq,
V: ?Sized + Storable + PartialEq,
P: BTreeMutPage<K, V> + BTreePage<K, V>,
> {
pub(crate) tx: &'a Tx,
pub id: usize,
pub(crate) _kvp: PhantomData<(&'a K, &'a V, &'a P)>,
}
sanakirja
Copy-on-write datastructures, storable on disk (or elsewhere) with a stable format.