diff options
Diffstat (limited to 'src/db')
-rw-r--r-- | src/db/Cargo.toml | 13 | ||||
-rw-r--r-- | src/db/bin/convert.rs | 69 | ||||
-rw-r--r-- | src/db/counted_tree_hack.rs | 6 | ||||
-rw-r--r-- | src/db/lib.rs | 78 | ||||
-rw-r--r-- | src/db/lmdb_adapter.rs | 10 | ||||
-rw-r--r-- | src/db/sled_adapter.rs | 20 | ||||
-rw-r--r-- | src/db/sqlite_adapter.rs | 10 | ||||
-rw-r--r-- | src/db/test.rs | 8 |
8 files changed, 75 insertions, 139 deletions
diff --git a/src/db/Cargo.toml b/src/db/Cargo.toml index 67a4fd5e..67af4a7c 100644 --- a/src/db/Cargo.toml +++ b/src/db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "garage_db" -version = "0.8.2" +version = "0.9.0" authors = ["Alex Auvolat <alex@adnab.me>"] edition = "2018" license = "AGPL-3.0" @@ -11,29 +11,24 @@ readme = "../../README.md" [lib] path = "lib.rs" -[[bin]] -name = "convert" -path = "bin/convert.rs" -required-features = ["cli"] - [dependencies] err-derive = "0.3" hexdump = "0.1" tracing = "0.1" heed = { version = "0.11", default-features = false, features = ["lmdb"], optional = true } -rusqlite = { version = "0.28", optional = true } +rusqlite = { version = "0.29", optional = true } sled = { version = "0.34", optional = true } # cli deps clap = { version = "4.1", optional = true, features = ["derive", "env"] } -pretty_env_logger = { version = "0.4", optional = true } +pretty_env_logger = { version = "0.5", optional = true } [dev-dependencies] mktemp = "0.5" [features] -default = [ "sled" ] +default = [ "sled", "lmdb", "sqlite" ] bundled-libs = [ "rusqlite?/bundled" ] cli = ["clap", "pretty_env_logger"] lmdb = [ "heed" ] diff --git a/src/db/bin/convert.rs b/src/db/bin/convert.rs deleted file mode 100644 index bbde2048..00000000 --- a/src/db/bin/convert.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::path::PathBuf; - -use garage_db::*; - -use clap::Parser; - -/// K2V command line interface -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] -struct Args { - /// Input DB path - #[clap(short = 'i')] - input_path: PathBuf, - /// Input DB engine - #[clap(short = 'a')] - input_engine: String, - - /// Output DB path - #[clap(short = 'o')] - output_path: PathBuf, - /// Output DB engine - #[clap(short = 'b')] - output_engine: String, -} - -fn main() { - let args = Args::parse(); - pretty_env_logger::init(); - - match do_conversion(args) { - Ok(()) => println!("Success!"), - Err(e) => eprintln!("Error: {}", e), - } -} - -fn do_conversion(args: Args) -> Result<()> { - let input = open_db(args.input_path, args.input_engine)?; - let output = open_db(args.output_path, args.output_engine)?; - output.import(&input)?; - Ok(()) -} - -fn open_db(path: PathBuf, engine: String) -> Result<Db> { - match engine.as_str() { - "sled" => { - let db = sled_adapter::sled::Config::default().path(&path).open()?; - Ok(sled_adapter::SledDb::init(db)) - } - "sqlite" | "sqlite3" | "rusqlite" => { - let db = sqlite_adapter::rusqlite::Connection::open(&path)?; - Ok(sqlite_adapter::SqliteDb::init(db)) - } - "lmdb" | "heed" => { - std::fs::create_dir_all(&path).map_err(|e| { - Error(format!("Unable to create LMDB data directory: {}", e).into()) - })?; - - let map_size = lmdb_adapter::recommended_map_size(); - - let db = lmdb_adapter::heed::EnvOpenOptions::new() - .max_dbs(100) - .map_size(map_size) - .open(&path) - .unwrap(); - Ok(lmdb_adapter::LmdbDb::init(db)) - } - e => Err(Error(format!("Invalid DB engine: {}", e).into())), - } -} diff --git a/src/db/counted_tree_hack.rs b/src/db/counted_tree_hack.rs index bbe943a2..a4ce12e0 100644 --- a/src/db/counted_tree_hack.rs +++ b/src/db/counted_tree_hack.rs @@ -85,7 +85,7 @@ impl CountedTree { let old_some = expected_old.is_some(); let new_some = new.is_some(); - let tx_res = self.0.tree.db().transaction(|mut tx| { + let tx_res = self.0.tree.db().transaction(|tx| { let old_val = tx.get(&self.0.tree, &key)?; let is_same = match (&old_val, &expected_old) { (None, None) => true, @@ -101,9 +101,9 @@ impl CountedTree { tx.remove(&self.0.tree, &key)?; } } - tx.commit(()) + Ok(()) } else { - tx.abort(()) + Err(TxError::Abort(())) } }); diff --git a/src/db/lib.rs b/src/db/lib.rs index 11cae4e3..fe44b01e 100644 --- a/src/db/lib.rs +++ b/src/db/lib.rs @@ -2,9 +2,6 @@ #[cfg(feature = "sqlite")] extern crate tracing; -#[cfg(not(any(feature = "lmdb", feature = "sled", feature = "sqlite")))] -compile_error!("Must activate the Cargo feature for at least one DB engine: lmdb, sled or sqlite."); - #[cfg(feature = "lmdb")] pub mod lmdb_adapter; #[cfg(feature = "sled")] @@ -25,10 +22,15 @@ use std::sync::Arc; use err_derive::Error; +pub(crate) type OnCommit = Vec<Box<dyn FnOnce()>>; + #[derive(Clone)] pub struct Db(pub(crate) Arc<dyn IDb>); -pub struct Transaction<'a>(&'a mut dyn ITx); +pub struct Transaction<'a> { + tx: &'a mut dyn ITx, + on_commit: OnCommit, +} #[derive(Clone)] pub struct Tree(Arc<dyn IDb>, usize); @@ -88,7 +90,7 @@ impl Db { pub fn transaction<R, E, F>(&self, fun: F) -> TxResult<R, E> where - F: Fn(Transaction<'_>) -> TxResult<R, E>, + F: Fn(&mut Transaction<'_>) -> TxResult<R, E>, { let f = TxFn { function: fun, @@ -101,14 +103,17 @@ impl Db { .expect("Transaction did not store result"); match tx_res { - Ok(()) => { - assert!(matches!(ret, Ok(_))); - ret - } - Err(TxError::Abort(())) => { - assert!(matches!(ret, Err(TxError::Abort(_)))); - ret - } + Ok(on_commit) => match ret { + Ok(value) => { + on_commit.into_iter().for_each(|f| f()); + Ok(value) + } + _ => unreachable!(), + }, + Err(TxError::Abort(())) => match ret { + Err(TxError::Abort(e)) => Err(TxError::Abort(e)), + _ => unreachable!(), + }, Err(TxError::Db(e2)) => match ret { // Ok was stored -> the error occured when finalizing // transaction @@ -142,7 +147,7 @@ impl Db { let ex_tree = other.open_tree(&name)?; - let tx_res = self.transaction(|mut tx| { + let tx_res = self.transaction(|tx| { let mut i = 0; for item in ex_tree.iter().map_err(TxError::Abort)? { let (k, v) = item.map_err(TxError::Abort)?; @@ -152,7 +157,7 @@ impl Db { println!("{}: imported {}", name, i); } } - tx.commit(i) + Ok(i) }); let total = match tx_res { Err(TxError::Db(e)) => return Err(e), @@ -252,11 +257,11 @@ impl Tree { impl<'a> Transaction<'a> { #[inline] pub fn get<T: AsRef<[u8]>>(&self, tree: &Tree, key: T) -> TxOpResult<Option<Value>> { - self.0.get(tree.1, key.as_ref()) + self.tx.get(tree.1, key.as_ref()) } #[inline] pub fn len(&self, tree: &Tree) -> TxOpResult<usize> { - self.0.len(tree.1) + self.tx.len(tree.1) } /// Returns the old value if there was one @@ -267,21 +272,21 @@ impl<'a> Transaction<'a> { key: T, value: U, ) -> TxOpResult<Option<Value>> { - self.0.insert(tree.1, key.as_ref(), value.as_ref()) + self.tx.insert(tree.1, key.as_ref(), value.as_ref()) } /// Returns the old value if there was one #[inline] pub fn remove<T: AsRef<[u8]>>(&mut self, tree: &Tree, key: T) -> TxOpResult<Option<Value>> { - self.0.remove(tree.1, key.as_ref()) + self.tx.remove(tree.1, key.as_ref()) } #[inline] pub fn iter(&self, tree: &Tree) -> TxOpResult<TxValueIter<'_>> { - self.0.iter(tree.1) + self.tx.iter(tree.1) } #[inline] pub fn iter_rev(&self, tree: &Tree) -> TxOpResult<TxValueIter<'_>> { - self.0.iter_rev(tree.1) + self.tx.iter_rev(tree.1) } #[inline] @@ -292,7 +297,7 @@ impl<'a> Transaction<'a> { { let sb = range.start_bound(); let eb = range.end_bound(); - self.0.range(tree.1, get_bound(sb), get_bound(eb)) + self.tx.range(tree.1, get_bound(sb), get_bound(eb)) } #[inline] pub fn range_rev<K, R>(&self, tree: &Tree, range: R) -> TxOpResult<TxValueIter<'_>> @@ -302,19 +307,12 @@ impl<'a> Transaction<'a> { { let sb = range.start_bound(); let eb = range.end_bound(); - self.0.range_rev(tree.1, get_bound(sb), get_bound(eb)) + self.tx.range_rev(tree.1, get_bound(sb), get_bound(eb)) } - // ---- - #[inline] - pub fn abort<R, E>(self, e: E) -> TxResult<R, E> { - Err(TxError::Abort(e)) - } - - #[inline] - pub fn commit<R, E>(self, r: R) -> TxResult<R, E> { - Ok(r) + pub fn on_commit<F: FnOnce() + 'static>(&mut self, f: F) { + self.on_commit.push(Box::new(f)); } } @@ -351,7 +349,7 @@ pub(crate) trait IDb: Send + Sync { high: Bound<&'r [u8]>, ) -> Result<ValueIter<'_>>; - fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()>; + fn transaction(&self, f: &dyn ITxFn) -> TxResult<OnCommit, ()>; } pub(crate) trait ITx { @@ -383,14 +381,14 @@ pub(crate) trait ITxFn { } pub(crate) enum TxFnResult { - Ok, + Ok(OnCommit), Abort, DbErr, } struct TxFn<F, R, E> where - F: Fn(Transaction<'_>) -> TxResult<R, E>, + F: Fn(&mut Transaction<'_>) -> TxResult<R, E>, { function: F, result: Cell<Option<TxResult<R, E>>>, @@ -398,12 +396,16 @@ where impl<F, R, E> ITxFn for TxFn<F, R, E> where - F: Fn(Transaction<'_>) -> TxResult<R, E>, + F: Fn(&mut Transaction<'_>) -> TxResult<R, E>, { fn try_on(&self, tx: &mut dyn ITx) -> TxFnResult { - let res = (self.function)(Transaction(tx)); + let mut tx = Transaction { + tx, + on_commit: vec![], + }; + let res = (self.function)(&mut tx); let res2 = match &res { - Ok(_) => TxFnResult::Ok, + Ok(_) => TxFnResult::Ok(tx.on_commit), Err(TxError::Abort(_)) => TxFnResult::Abort, Err(TxError::Db(_)) => TxFnResult::DbErr, }; diff --git a/src/db/lmdb_adapter.rs b/src/db/lmdb_adapter.rs index ecbc3b81..59fa132d 100644 --- a/src/db/lmdb_adapter.rs +++ b/src/db/lmdb_adapter.rs @@ -9,8 +9,8 @@ use heed::types::ByteSlice; use heed::{BytesDecode, Env, RoTxn, RwTxn, UntypedDatabase as Database}; use crate::{ - Db, Error, IDb, ITx, ITxFn, Result, TxError, TxFnResult, TxOpError, TxOpResult, TxResult, - TxValueIter, Value, ValueIter, + Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult, + TxResult, TxValueIter, Value, ValueIter, }; pub use heed; @@ -186,7 +186,7 @@ impl IDb for LmdbDb { // ---- - fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()> { + fn transaction(&self, f: &dyn ITxFn) -> TxResult<OnCommit, ()> { let trees = self.trees.read().unwrap(); let mut tx = LmdbTx { trees: &trees.0[..], @@ -199,9 +199,9 @@ impl IDb for LmdbDb { let res = f.try_on(&mut tx); match res { - TxFnResult::Ok => { + TxFnResult::Ok(on_commit) => { tx.tx.commit().map_err(Error::from).map_err(TxError::Db)?; - Ok(()) + Ok(on_commit) } TxFnResult::Abort => { tx.tx.abort().map_err(Error::from).map_err(TxError::Db)?; diff --git a/src/db/sled_adapter.rs b/src/db/sled_adapter.rs index cf61867d..84f2001b 100644 --- a/src/db/sled_adapter.rs +++ b/src/db/sled_adapter.rs @@ -10,8 +10,8 @@ use sled::transaction::{ }; use crate::{ - Db, Error, IDb, ITx, ITxFn, Result, TxError, TxFnResult, TxOpError, TxOpResult, TxResult, - TxValueIter, Value, ValueIter, + Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult, + TxResult, TxValueIter, Value, ValueIter, }; pub use sled; @@ -38,7 +38,15 @@ pub struct SledDb { } impl SledDb { + #[deprecated( + since = "0.9.0", + note = "The Sled database is now deprecated and will be removed in Garage v1.0. Please migrate to LMDB or Sqlite as soon as possible." + )] pub fn init(db: sled::Db) -> Db { + tracing::warn!("-------------------- IMPORTANT WARNING !!! ----------------------"); + tracing::warn!("The Sled database is now deprecated and will be removed in Garage v1.0."); + tracing::warn!("Please migrate to LMDB or Sqlite as soon as possible."); + tracing::warn!("-----------------------------------------------------------------------"); let s = Self { db, trees: RwLock::new((Vec::new(), HashMap::new())), @@ -158,7 +166,7 @@ impl IDb for SledDb { // ---- - fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()> { + fn transaction(&self, f: &dyn ITxFn) -> TxResult<OnCommit, ()> { let trees = self.trees.read().unwrap(); let res = trees.0.transaction(|txtrees| { let mut tx = SledTx { @@ -166,9 +174,9 @@ impl IDb for SledDb { err: Cell::new(None), }; match f.try_on(&mut tx) { - TxFnResult::Ok => { + TxFnResult::Ok(on_commit) => { assert!(tx.err.into_inner().is_none()); - Ok(()) + Ok(on_commit) } TxFnResult::Abort => { assert!(tx.err.into_inner().is_none()); @@ -181,7 +189,7 @@ impl IDb for SledDb { } }); match res { - Ok(()) => Ok(()), + Ok(on_commit) => Ok(on_commit), Err(TransactionError::Abort(())) => Err(TxError::Abort(())), Err(TransactionError::Storage(s)) => Err(TxError::Db(s.into())), } diff --git a/src/db/sqlite_adapter.rs b/src/db/sqlite_adapter.rs index 63b4506e..9f967c66 100644 --- a/src/db/sqlite_adapter.rs +++ b/src/db/sqlite_adapter.rs @@ -9,8 +9,8 @@ use std::sync::{Arc, Mutex, MutexGuard}; use rusqlite::{params, Connection, Rows, Statement, Transaction}; use crate::{ - Db, Error, IDb, ITx, ITxFn, Result, TxError, TxFnResult, TxOpError, TxOpResult, TxResult, - TxValueIter, Value, ValueIter, + Db, Error, IDb, ITx, ITxFn, OnCommit, Result, TxError, TxFnResult, TxOpError, TxOpResult, + TxResult, TxValueIter, Value, ValueIter, }; pub use rusqlite; @@ -261,7 +261,7 @@ impl IDb for SqliteDb { // ---- - fn transaction(&self, f: &dyn ITxFn) -> TxResult<(), ()> { + fn transaction(&self, f: &dyn ITxFn) -> TxResult<OnCommit, ()> { trace!("transaction: lock db"); let mut this = self.0.lock().unwrap(); trace!("transaction: lock acquired"); @@ -277,9 +277,9 @@ impl IDb for SqliteDb { trees: &this_mut_ref.trees, }; let res = match f.try_on(&mut tx) { - TxFnResult::Ok => { + TxFnResult::Ok(on_commit) => { tx.tx.commit().map_err(Error::from).map_err(TxError::Db)?; - Ok(()) + Ok(on_commit) } TxFnResult::Abort => { tx.tx.rollback().map_err(Error::from).map_err(TxError::Db)?; diff --git a/src/db/test.rs b/src/db/test.rs index 40e6c41e..cd99eafa 100644 --- a/src/db/test.rs +++ b/src/db/test.rs @@ -13,26 +13,26 @@ fn test_suite(db: Db) { assert!(tree.insert(ka, va).unwrap().is_none()); assert_eq!(tree.get(ka).unwrap().unwrap(), va); - let res = db.transaction::<_, (), _>(|mut tx| { + let res = db.transaction::<_, (), _>(|tx| { assert_eq!(tx.get(&tree, ka).unwrap().unwrap(), va); assert_eq!(tx.insert(&tree, ka, vb).unwrap().unwrap(), va); assert_eq!(tx.get(&tree, ka).unwrap().unwrap(), vb); - tx.commit(12) + Ok(12) }); assert!(matches!(res, Ok(12))); assert_eq!(tree.get(ka).unwrap().unwrap(), vb); - let res = db.transaction::<(), _, _>(|mut tx| { + let res = db.transaction::<(), _, _>(|tx| { assert_eq!(tx.get(&tree, ka).unwrap().unwrap(), vb); assert_eq!(tx.insert(&tree, ka, vc).unwrap().unwrap(), vb); assert_eq!(tx.get(&tree, ka).unwrap().unwrap(), vc); - tx.abort(42) + Err(TxError::Abort(42)) }); assert!(matches!(res, Err(TxError::Abort(42)))); assert_eq!(tree.get(ka).unwrap().unwrap(), vb); |