aboutsummaryrefslogtreecommitdiff
path: root/src/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/db')
-rw-r--r--src/db/counted_tree_hack.rs130
-rw-r--r--src/db/lib.rs16
-rw-r--r--src/db/lmdb_adapter.rs10
-rw-r--r--src/db/sled_adapter.rs12
-rw-r--r--src/db/sqlite_adapter.rs43
5 files changed, 180 insertions, 31 deletions
diff --git a/src/db/counted_tree_hack.rs b/src/db/counted_tree_hack.rs
new file mode 100644
index 00000000..52893b41
--- /dev/null
+++ b/src/db/counted_tree_hack.rs
@@ -0,0 +1,130 @@
+//! This hack allows a db tree to keep in RAM a counter of the number of entries
+//! it contains, which is used to call .len() on it. This is usefull only for
+//! the sled backend where .len() otherwise would have to traverse the whole
+//! tree to count items. For sqlite and lmdb, this is mostly useless (but
+//! hopefully not harmfull!). Note that a CountedTree cannot be part of a
+//! transaction.
+
+use std::sync::{
+ atomic::{AtomicUsize, Ordering},
+ Arc,
+};
+
+use crate::{Result, Tree, TxError, Value, ValueIter};
+
+#[derive(Clone)]
+pub struct CountedTree(Arc<CountedTreeInternal>);
+
+struct CountedTreeInternal {
+ tree: Tree,
+ len: AtomicUsize,
+}
+
+impl CountedTree {
+ pub fn new(tree: Tree) -> Result<Self> {
+ let len = tree.len()?;
+ Ok(Self(Arc::new(CountedTreeInternal {
+ tree,
+ len: AtomicUsize::new(len),
+ })))
+ }
+
+ pub fn len(&self) -> usize {
+ self.0.len.load(Ordering::Relaxed)
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ pub fn get<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<Value>> {
+ self.0.tree.get(key)
+ }
+
+ pub fn first(&self) -> Result<Option<(Value, Value)>> {
+ self.0.tree.first()
+ }
+
+ pub fn iter(&self) -> Result<ValueIter<'_>> {
+ self.0.tree.iter()
+ }
+
+ // ---- writing functions ----
+
+ pub fn insert<K, V>(&self, key: K, value: V) -> Result<bool>
+ where
+ K: AsRef<[u8]>,
+ V: AsRef<[u8]>,
+ {
+ let inserted = self.0.tree.insert(key, value)?;
+ if inserted {
+ self.0.len.fetch_add(1, Ordering::Relaxed);
+ }
+ Ok(inserted)
+ }
+
+ pub fn remove<K: AsRef<[u8]>>(&self, key: K) -> Result<bool> {
+ let removed = self.0.tree.remove(key)?;
+ if removed {
+ self.0.len.fetch_sub(1, Ordering::Relaxed);
+ }
+ Ok(removed)
+ }
+
+ /*
+ pub fn pop_min(&self) -> Result<Option<(Value, Value)>> {
+ let res = self.0.tree.pop_min();
+ if let Ok(Some(_)) = &res {
+ self.0.len.fetch_sub(1, Ordering::Relaxed);
+ };
+ res
+ }
+ */
+
+ pub fn compare_and_swap<K, OV, NV>(
+ &self,
+ key: K,
+ expected_old: Option<OV>,
+ new: Option<NV>,
+ ) -> Result<bool>
+ where
+ K: AsRef<[u8]>,
+ OV: AsRef<[u8]>,
+ NV: AsRef<[u8]>,
+ {
+ let old_some = expected_old.is_some();
+ let new_some = new.is_some();
+
+ match self.0.tree.db().transaction(|mut tx| {
+ let old_val = tx.get(&self.0.tree, &key)?;
+ if old_val.as_ref().map(|x| &x[..]) == expected_old.as_ref().map(AsRef::as_ref) {
+ match &new {
+ Some(v) => {
+ tx.insert(&self.0.tree, &key, v)?;
+ }
+ None => {
+ tx.remove(&self.0.tree, &key)?;
+ }
+ }
+ tx.commit(())
+ } else {
+ tx.abort(())
+ }
+ }) {
+ Ok(()) => {
+ match (old_some, new_some) {
+ (false, true) => {
+ self.0.len.fetch_add(1, Ordering::Relaxed);
+ }
+ (true, false) => {
+ self.0.len.fetch_sub(1, Ordering::Relaxed);
+ }
+ _ => (),
+ }
+ Ok(true)
+ }
+ Err(TxError::Abort(())) => Ok(false),
+ Err(TxError::Db(e)) => Err(e),
+ }
+ }
+}
diff --git a/src/db/lib.rs b/src/db/lib.rs
index afea9f55..4543e53c 100644
--- a/src/db/lib.rs
+++ b/src/db/lib.rs
@@ -2,6 +2,8 @@ pub mod lmdb_adapter;
pub mod sled_adapter;
pub mod sqlite_adapter;
+pub mod counted_tree_hack;
+
#[cfg(test)]
pub mod test;
@@ -164,10 +166,13 @@ impl Tree {
.transpose()
}
+ /// True if item didn't exist before, false if item already existed
+ /// and was replaced.
#[inline]
- pub fn insert<T: AsRef<[u8]>, U: AsRef<[u8]>>(&self, key: T, value: U) -> Result<()> {
+ pub fn insert<T: AsRef<[u8]>, U: AsRef<[u8]>>(&self, key: T, value: U) -> Result<bool> {
self.0.insert(self.1, key.as_ref(), value.as_ref())
}
+ /// True if item was removed, false if item already didn't exist
#[inline]
pub fn remove<T: AsRef<[u8]>>(&self, key: T) -> Result<bool> {
self.0.remove(self.1, key.as_ref())
@@ -215,15 +220,18 @@ impl<'a> Transaction<'a> {
self.0.len(tree.1)
}
+ /// True if item didn't exist before, false if item already existed
+ /// and was replaced.
#[inline]
pub fn insert<T: AsRef<[u8]>, U: AsRef<[u8]>>(
&mut self,
tree: &Tree,
key: T,
value: U,
- ) -> Result<()> {
+ ) -> Result<bool> {
self.0.insert(tree.1, key.as_ref(), value.as_ref())
}
+ /// True if item was removed, false if item already didn't exist
#[inline]
pub fn remove<T: AsRef<[u8]>>(&mut self, tree: &Tree, key: T) -> Result<bool> {
self.0.remove(tree.1, key.as_ref())
@@ -281,7 +289,7 @@ pub(crate) trait IDb: Send + Sync {
fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value>>;
fn len(&self, tree: usize) -> Result<usize>;
- fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>;
+ fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<bool>;
fn remove(&self, tree: usize, key: &[u8]) -> Result<bool>;
fn iter(&self, tree: usize) -> Result<ValueIter<'_>>;
@@ -307,7 +315,7 @@ pub(crate) trait ITx {
fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value>>;
fn len(&self, tree: usize) -> Result<usize>;
- fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> Result<()>;
+ fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> Result<bool>;
fn remove(&mut self, tree: usize, key: &[u8]) -> Result<bool>;
fn iter(&self, tree: usize) -> Result<ValueIter<'_>>;
diff --git a/src/db/lmdb_adapter.rs b/src/db/lmdb_adapter.rs
index 3f468128..d8e5bcd3 100644
--- a/src/db/lmdb_adapter.rs
+++ b/src/db/lmdb_adapter.rs
@@ -122,12 +122,13 @@ impl IDb for LmdbDb {
Ok(tree.len(&tx)?.try_into().unwrap())
}
- fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
+ fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<bool> {
let tree = self.get_tree(tree)?;
let mut tx = self.db.write_txn()?;
+ let old_val = tree.get(&tx, key)?.map(Vec::from);
tree.put(&mut tx, key, value)?;
tx.commit()?;
- Ok(())
+ Ok(old_val.is_none())
}
fn iter(&self, tree: usize) -> Result<ValueIter<'_>> {
@@ -221,10 +222,11 @@ impl<'a, 'db> ITx for LmdbTx<'a, 'db> {
unimplemented!(".len() in transaction not supported with LMDB backend")
}
- fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
+ fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> Result<bool> {
let tree = *self.get_tree(tree)?;
+ let old_val = tree.get(&self.tx, key)?.map(Vec::from);
tree.put(&mut self.tx, key, value)?;
- Ok(())
+ Ok(old_val.is_none())
}
fn remove(&mut self, tree: usize, key: &[u8]) -> Result<bool> {
let tree = *self.get_tree(tree)?;
diff --git a/src/db/sled_adapter.rs b/src/db/sled_adapter.rs
index 97fec2c7..18f457c8 100644
--- a/src/db/sled_adapter.rs
+++ b/src/db/sled_adapter.rs
@@ -93,10 +93,10 @@ impl IDb for SledDb {
Ok(tree.len())
}
- fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
+ fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<bool> {
let tree = self.get_tree(tree)?;
- tree.insert(key, value)?;
- Ok(())
+ let old_val = tree.insert(key, value)?;
+ Ok(old_val.is_none())
}
fn iter(&self, tree: usize) -> Result<ValueIter<'_>> {
@@ -206,10 +206,10 @@ impl<'a> ITx for SledTx<'a> {
unimplemented!(".len() in transaction not supported with Sled backend")
}
- fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
+ fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> Result<bool> {
let tree = self.get_tree(tree)?;
- self.save_error(tree.insert(key, value))?;
- Ok(())
+ let old_val = self.save_error(tree.insert(key, value))?;
+ Ok(old_val.is_none())
}
fn remove(&mut self, tree: usize, key: &[u8]) -> Result<bool> {
let tree = self.get_tree(tree)?;
diff --git a/src/db/sqlite_adapter.rs b/src/db/sqlite_adapter.rs
index 0c8a0746..32557a53 100644
--- a/src/db/sqlite_adapter.rs
+++ b/src/db/sqlite_adapter.rs
@@ -54,6 +54,17 @@ impl SqliteDbInner {
.map(String::as_str)
.ok_or_else(|| Error("invalid tree id".into()))
}
+
+ fn internal_get(&self, tree: &str, key: &[u8]) -> Result<Option<Value>> {
+ let mut stmt = self
+ .db
+ .prepare(&format!("SELECT v FROM {} WHERE k = ?1", tree))?;
+ let mut res_iter = stmt.query([key])?;
+ match res_iter.next()? {
+ None => Ok(None),
+ Some(v) => Ok(Some(v.get::<_, Vec<u8>>(0)?)),
+ }
+ }
}
impl IDb for SqliteDb {
@@ -111,15 +122,7 @@ impl IDb for SqliteDb {
trace!("get {}: lock acquired", tree);
let tree = this.get_tree(tree)?;
-
- let mut stmt = this
- .db
- .prepare(&format!("SELECT v FROM {} WHERE k = ?1", tree))?;
- let mut res_iter = stmt.query([key])?;
- match res_iter.next()? {
- None => Ok(None),
- Some(v) => Ok(Some(v.get::<_, Vec<u8>>(0)?)),
- }
+ this.internal_get(tree, key)
}
fn remove(&self, tree: usize, key: &[u8]) -> Result<bool> {
@@ -148,17 +151,18 @@ impl IDb for SqliteDb {
}
}
- fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
+ fn insert(&self, tree: usize, key: &[u8], value: &[u8]) -> Result<bool> {
trace!("insert {}: lock db", tree);
let this = self.0.lock().unwrap();
trace!("insert {}: lock acquired", tree);
let tree = this.get_tree(tree)?;
+ let old_val = this.internal_get(tree, key)?;
this.db.execute(
&format!("INSERT OR REPLACE INTO {} (k, v) VALUES (?1, ?2)", tree),
params![key, value],
)?;
- Ok(())
+ Ok(old_val.is_none())
}
fn iter(&self, tree: usize) -> Result<ValueIter<'_>> {
@@ -276,11 +280,8 @@ impl<'a> SqliteTx<'a> {
)
})
}
-}
-impl<'a> ITx for SqliteTx<'a> {
- fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value>> {
- let tree = self.get_tree(tree)?;
+ fn internal_get(&self, tree: &str, key: &[u8]) -> Result<Option<Value>> {
let mut stmt = self
.tx
.prepare(&format!("SELECT v FROM {} WHERE k = ?1", tree))?;
@@ -290,6 +291,13 @@ impl<'a> ITx for SqliteTx<'a> {
Some(v) => Ok(Some(v.get::<_, Vec<u8>>(0)?)),
}
}
+}
+
+impl<'a> ITx for SqliteTx<'a> {
+ fn get(&self, tree: usize, key: &[u8]) -> Result<Option<Value>> {
+ let tree = self.get_tree(tree)?;
+ self.internal_get(tree, key)
+ }
fn len(&self, tree: usize) -> Result<usize> {
let tree = self.get_tree(tree)?;
let mut stmt = self.tx.prepare(&format!("SELECT COUNT(*) FROM {}", tree))?;
@@ -300,13 +308,14 @@ impl<'a> ITx for SqliteTx<'a> {
}
}
- fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> Result<()> {
+ fn insert(&mut self, tree: usize, key: &[u8], value: &[u8]) -> Result<bool> {
let tree = self.get_tree(tree)?;
+ let old_val = self.internal_get(tree, key)?;
self.tx.execute(
&format!("INSERT OR REPLACE INTO {} (k, v) VALUES (?1, ?2)", tree),
params![key, value],
)?;
- Ok(())
+ Ok(old_val.is_none())
}
fn remove(&mut self, tree: usize, key: &[u8]) -> Result<bool> {
let tree = self.get_tree(tree)?;