Abstract database behind generic interface and implement alternative drivers #322
3 changed files with 98 additions and 15 deletions
|
@ -18,6 +18,7 @@ pub struct Transaction<'a>(pub(crate) &'a dyn ITx<'a>);
|
||||||
pub struct Tree(pub(crate) Arc<dyn IDb>, pub(crate) usize);
|
pub struct Tree(pub(crate) Arc<dyn IDb>, pub(crate) usize);
|
||||||
|
|
||||||
pub type Value<'a> = Cow<'a, [u8]>;
|
pub type Value<'a> = Cow<'a, [u8]>;
|
||||||
|
pub type ValueIter<'a> = Box<dyn std::iter::Iterator<Item = Result<(Value<'a>, Value<'a>)>> + 'a>;
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
|
||||||
|
@ -89,6 +90,14 @@ impl Tree {
|
||||||
pub fn put<T: AsRef<[u8]>, U: AsRef<[u8]>>(&self, key: T, value: U) -> Result<()> {
|
pub fn put<T: AsRef<[u8]>, U: AsRef<[u8]>>(&self, key: T, value: U) -> Result<()> {
|
||||||
self.0.put(self.1, key.as_ref(), value.as_ref())
|
self.0.put(self.1, key.as_ref(), value.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self, reverse: bool) -> Result<ValueIter<'a>> {
|
||||||
|
self.0.range(self.1, None, reverse)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range<'a, T: AsRef<[u8]>>(&'a self, start: T, reverse: bool) -> Result<ValueIter<'a>> {
|
||||||
|
self.0.range(self.1, Some(start.as_ref()), reverse)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Transaction<'a> {
|
impl<'a> Transaction<'a> {
|
||||||
|
@ -126,6 +135,12 @@ pub(crate) trait IDb: Send + Sync {
|
||||||
|
|
||||||
fn get<'a>(&'a self, tree: usize, key: &[u8]) -> Result<Option<Value<'a>>>;
|
fn get<'a>(&'a self, tree: usize, key: &[u8]) -> Result<Option<Value<'a>>>;
|
||||||
fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>;
|
fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>;
|
||||||
|
fn range<'a>(
|
||||||
|
&'a self,
|
||||||
|
tree: usize,
|
||||||
|
start: Option<&[u8]>,
|
||||||
|
reverse: bool,
|
||||||
|
) -> Result<ValueIter<'a>>;
|
||||||
|
|
||||||
fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()>;
|
fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,11 @@ use std::sync::{Arc, RwLock};
|
||||||
use arc_swap::ArcSwapOption;
|
use arc_swap::ArcSwapOption;
|
||||||
|
|
||||||
use sled::transaction::{
|
use sled::transaction::{
|
||||||
ConflictableTransactionError, TransactionError, Transactional, TransactionalTree, UnabortableTransactionError
|
ConflictableTransactionError, TransactionError, Transactional, TransactionalTree,
|
||||||
|
UnabortableTransactionError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Error, IDb, ITx, ITxFn, Result, TxError, TxFnResult, TxResult, Value, Db};
|
use crate::{Db, Error, IDb, ITx, ITxFn, Result, TxError, TxFnResult, TxResult, Value, ValueIter};
|
||||||
|
|
||||||
impl From<sled::Error> for Error {
|
impl From<sled::Error> for Error {
|
||||||
fn from(e: sled::Error) -> Error {
|
fn from(e: sled::Error) -> Error {
|
||||||
|
@ -58,12 +59,45 @@ impl IDb for SledDb {
|
||||||
let tree = self.get_tree(tree)?;
|
let tree = self.get_tree(tree)?;
|
||||||
Ok(tree.get(key)?.map(|v| v.to_vec().into()))
|
Ok(tree.get(key)?.map(|v| v.to_vec().into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
|
fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
|
||||||
let tree = self.get_tree(tree)?;
|
let tree = self.get_tree(tree)?;
|
||||||
tree.insert(key, value)?;
|
tree.insert(key, value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn range<'a>(
|
||||||
|
&'a self,
|
||||||
|
tree: usize,
|
||||||
|
start: Option<&[u8]>,
|
||||||
|
reverse: bool,
|
||||||
|
) -> Result<ValueIter<'a>> {
|
||||||
|
let tree = self.get_tree(tree)?;
|
||||||
|
if reverse {
|
||||||
|
match start {
|
||||||
|
Some(start) => Ok(Box::new(tree.range(..=start).rev().map(|v| {
|
||||||
|
v.map(|(x, y)| (x.to_vec().into(), y.to_vec().into()))
|
||||||
|
.map_err(Into::into)
|
||||||
|
}))),
|
||||||
|
None => Ok(Box::new(tree.iter().rev().map(|v| {
|
||||||
|
v.map(|(x, y)| (x.to_vec().into(), y.to_vec().into()))
|
||||||
|
.map_err(Into::into)
|
||||||
|
}))),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match start {
|
||||||
|
Some(start) => Ok(Box::new(tree.range(start..).map(|v| {
|
||||||
|
v.map(|(x, y)| (x.to_vec().into(), y.to_vec().into()))
|
||||||
|
.map_err(Into::into)
|
||||||
|
}))),
|
||||||
|
None => Ok(Box::new(tree.iter().map(|v| {
|
||||||
|
v.map(|(x, y)| (x.to_vec().into(), y.to_vec().into()))
|
||||||
|
.map_err(Into::into)
|
||||||
|
}))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()> {
|
fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()> {
|
||||||
let trees = self.trees.read().unwrap();
|
let trees = self.trees.read().unwrap();
|
||||||
let res = trees.0.transaction(|txtrees| {
|
let res = trees.0.transaction(|txtrees| {
|
||||||
|
@ -76,7 +110,10 @@ impl IDb for SledDb {
|
||||||
assert!(tx.err.into_inner().is_none());
|
assert!(tx.err.into_inner().is_none());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
TxFnResult::Abort => Err(ConflictableTransactionError::Abort(())),
|
TxFnResult::Abort => {
|
||||||
|
assert!(tx.err.into_inner().is_none());
|
||||||
|
Err(ConflictableTransactionError::Abort(()))
|
||||||
|
}
|
||||||
TxFnResult::Err => {
|
TxFnResult::Err => {
|
||||||
let err_arc = tx
|
let err_arc = tx
|
||||||
.err
|
.err
|
||||||
|
@ -117,14 +154,18 @@ impl<'a> SledTx<'a> {
|
||||||
|
|
||||||
impl<'a> ITx<'a> for SledTx<'a> {
|
impl<'a> ITx<'a> for SledTx<'a> {
|
||||||
fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value<'a>>> {
|
fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value<'a>>> {
|
||||||
let tree = self.trees.get(tree)
|
let tree = self
|
||||||
|
.trees
|
||||||
|
.get(tree)
|
||||||
.ok_or(Error("invalid tree id".into()))?;
|
.ok_or(Error("invalid tree id".into()))?;
|
||||||
let tmp = self.save_error(tree.get(key))?;
|
let tmp = self.save_error(tree.get(key))?;
|
||||||
Ok(tmp.map(|v| v.to_vec().into()))
|
Ok(tmp.map(|v| v.to_vec().into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
|
fn put(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
|
||||||
let tree = self.trees.get(tree)
|
let tree = self
|
||||||
|
.trees
|
||||||
|
.get(tree)
|
||||||
.ok_or(Error("invalid tree id".into()))?;
|
.ok_or(Error("invalid tree id".into()))?;
|
||||||
self.save_error(tree.insert(key, value))?;
|
self.save_error(tree.insert(key, value))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -5,36 +5,63 @@ use crate::sled_adapter::SledDb;
|
||||||
fn test_suite(db: Db) -> Result<()> {
|
fn test_suite(db: Db) -> Result<()> {
|
||||||
let tree = db.tree("tree")?;
|
let tree = db.tree("tree")?;
|
||||||
|
|
||||||
|
let ka: &[u8] = &b"test"[..];
|
||||||
|
let kb: &[u8] = &b"zwello"[..];
|
||||||
let va: &[u8] = &b"plop"[..];
|
let va: &[u8] = &b"plop"[..];
|
||||||
let vb: &[u8] = &b"plip"[..];
|
let vb: &[u8] = &b"plip"[..];
|
||||||
let vc: &[u8] = &b"plup"[..];
|
let vc: &[u8] = &b"plup"[..];
|
||||||
|
|
||||||
tree.put(b"test", va)?;
|
tree.put(ka, va)?;
|
||||||
assert_eq!(tree.get(b"test")?, Some(va.into()));
|
assert_eq!(tree.get(ka)?, Some(va.into()));
|
||||||
|
|
||||||
let res = db.transaction::<_, (), _>(|tx| {
|
let res = db.transaction::<_, (), _>(|tx| {
|
||||||
assert_eq!(tx.get(&tree, b"test")?, Some(va.into()));
|
assert_eq!(tx.get(&tree, ka)?, Some(va.into()));
|
||||||
|
|
||||||
tx.put(&tree, b"test", vb)?;
|
tx.put(&tree, ka, vb)?;
|
||||||
|
|
||||||
assert_eq!(tx.get(&tree, b"test")?, Some(vb.into()));
|
assert_eq!(tx.get(&tree, ka)?, Some(vb.into()));
|
||||||
|
|
||||||
tx.commit(12)
|
tx.commit(12)
|
||||||
});
|
});
|
||||||
assert!(matches!(res, Ok(12)));
|
assert!(matches!(res, Ok(12)));
|
||||||
assert_eq!(tree.get(b"test")?, Some(vb.into()));
|
assert_eq!(tree.get(ka)?, Some(vb.into()));
|
||||||
|
|
||||||
let res = db.transaction::<(), _, _>(|tx| {
|
let res = db.transaction::<(), _, _>(|tx| {
|
||||||
assert_eq!(tx.get(&tree, b"test")?, Some(vb.into()));
|
assert_eq!(tx.get(&tree, ka)?, Some(vb.into()));
|
||||||
|
|
||||||
tx.put(&tree, b"test", vc)?;
|
tx.put(&tree, ka, vc)?;
|
||||||
|
|
||||||
assert_eq!(tx.get(&tree, b"test")?, Some(vc.into()));
|
assert_eq!(tx.get(&tree, ka)?, Some(vc.into()));
|
||||||
|
|
||||||
tx.abort(42)
|
tx.abort(42)
|
||||||
});
|
});
|
||||||
assert!(matches!(res, Err(TxError::Abort(42))));
|
assert!(matches!(res, Err(TxError::Abort(42))));
|
||||||
assert_eq!(tree.get(b"test")?, Some(vb.into()));
|
assert_eq!(tree.get(ka)?, Some(vb.into()));
|
||||||
|
|
||||||
|
let mut iter = tree.iter(false)?;
|
||||||
|
assert_eq!(iter.next().unwrap().unwrap(), (ka.into(), vb.into()));
|
||||||
|
assert!(iter.next().is_none());
|
||||||
|
|
||||||
|
tree.put(kb, vc)?;
|
||||||
|
assert_eq!(tree.get(kb)?, Some(vc.into()));
|
||||||
|
|
||||||
|
let mut iter = tree.iter(false)?;
|
||||||
|
assert_eq!(iter.next().unwrap().unwrap(), (ka.into(), vb.into()));
|
||||||
|
assert_eq!(iter.next().unwrap().unwrap(), (kb.into(), vc.into()));
|
||||||
|
assert!(iter.next().is_none());
|
||||||
|
|
||||||
|
let mut iter = tree.range("tz", false)?;
|
||||||
|
assert_eq!(iter.next().unwrap().unwrap(), (kb.into(), vc.into()));
|
||||||
|
assert!(iter.next().is_none());
|
||||||
|
|
||||||
|
let mut iter = tree.range("tz", true)?;
|
||||||
|
assert_eq!(iter.next().unwrap().unwrap(), (ka.into(), vb.into()));
|
||||||
|
assert!(iter.next().is_none());
|
||||||
|
|
||||||
|
let mut iter = tree.iter(true)?;
|
||||||
|
assert_eq!(iter.next().unwrap().unwrap(), (kb.into(), vc.into()));
|
||||||
|
assert_eq!(iter.next().unwrap().unwrap(), (ka.into(), vb.into()));
|
||||||
|
assert!(iter.next().is_none());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue