pub struct Tree(/* private fields */);
Expand description
A log-structured merge tree (LSM-tree/LSMT)
The tree is internally synchronized (Send + Sync), so it does not need to be wrapped in a lock nor an Arc.
To share the tree between threads, use Arc::clone(&tree)
or tree.clone()
.
Implementations§
source§impl Tree
impl Tree
sourcepub fn open(config: Config) -> Result<Self>
pub fn open(config: Config) -> Result<Self>
Opens the tree at the given folder.
Will create a new tree if the folder is not in use or recover a previous state if it exists.
Examples
use lsm_tree::{Config, Tree};
let tree = Tree::open(Config::new(folder))?;
// Same as
let tree = Config::new(folder).open()?;
Errors
Will return Err
if an IO error occurs.
sourcepub fn snapshot(&self) -> Snapshot
pub fn snapshot(&self) -> Snapshot
Opens a read-only point-in-time snapshot of the tree
Dropping the snapshot will close the snapshot
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
tree.insert("a", "abc")?;
let snapshot = tree.snapshot();
assert_eq!(snapshot.len()?, tree.len()?);
tree.insert("b", "abc")?;
assert_eq!(2, tree.len()?);
assert_eq!(1, snapshot.len()?);
assert!(snapshot.contains_key("a")?);
assert!(!snapshot.contains_key("b")?);
sourcepub fn batch(&self) -> Batch
pub fn batch(&self) -> Batch
Initializes a new, atomic write batch.
Call Batch::commit
to commit the batch to the tree.
Dropping the batch will not commit items to the tree.
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
let mut batch = tree.batch();
batch.insert("a", "hello");
batch.insert("b", "hello2");
batch.insert("c", "hello3");
batch.remove("idontlikeu");
batch.commit()?;
assert_eq!(3, tree.len()?);
sourcepub fn disk_space(&self) -> Result<u64>
pub fn disk_space(&self) -> Result<u64>
Sums the disk space usage of the tree (segments + journals).
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
assert_eq!(0, tree.disk_space()?);
sourcepub fn approximate_len(&self) -> Result<u64>
pub fn approximate_len(&self) -> Result<u64>
Approximates the item count of the tree.
This metric is only reliable for insert-only (no updates, deletes) workloads. Otherwise, the value may become less accurate over time and only converge to the real value time as compaction kicks in.
This operation has O(1) complexity and can be used without feeling bad about it.
sourcepub fn config(&self) -> Config
pub fn config(&self) -> Config
Returns the tree configuration.
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
assert_eq!(Config::default().block_size, tree.config().block_size);
sourcepub fn block_cache_size(&self) -> usize
pub fn block_cache_size(&self) -> usize
Returns the amount of cached blocks.
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
assert_eq!(0, tree.block_cache_size());
sourcepub fn len(&self) -> Result<usize>
pub fn len(&self) -> Result<usize>
Scans the entire tree, returning the amount of items.
Caution
This operation scans the entire tree: O(n) complexity!
Never, under any circumstances, use .len() == 0 to check
if the tree is empty, use Tree::is_empty
instead.
Examples
use lsm_tree::{Tree, Config};
let folder = tempfile::tempdir()?;
let tree = Config::new(folder).open()?;
assert_eq!(tree.len()?, 0);
tree.insert("1", "abc")?;
tree.insert("3", "abc")?;
tree.insert("5", "abc")?;
assert_eq!(tree.len()?, 3);
Errors
Will return Err
if an IO error occurs.
sourcepub fn remove<K: AsRef<[u8]>>(&self, key: K) -> Result<()>
pub fn remove<K: AsRef<[u8]>>(&self, key: K) -> Result<()>
Deletes an item from the tree.
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
tree.insert("a", "abc")?;
let item = tree.get("a")?.expect("should have item");
assert_eq!("abc".as_bytes(), &*item);
tree.remove("a")?;
let item = tree.get("a")?;
assert_eq!(None, item);
Errors
Will return Err
if an IO error occurs.
sourcepub fn remove_entry<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<Arc<[u8]>>>
pub fn remove_entry<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<Arc<[u8]>>>
Removes the item and returns its value if it was previously in the tree.
This is less efficient than just deleting because it needs to do a read before deleting.
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
let item = tree.remove_entry("a")?;
assert_eq!(None, item);
tree.insert("a", "abc")?;
let item = tree.remove_entry("a")?.expect("should have removed item");
assert_eq!("abc".as_bytes(), &*item);
Errors
Will return Err
if an IO error occurs.
sourcepub fn iter(&self) -> Range<'_>
pub fn iter(&self) -> Range<'_>
Returns an iterator that scans through the entire tree.
Avoid using this function, or limit it as otherwise it may scan a lot of items.
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
tree.insert("a", nanoid::nanoid!())?;
tree.insert("f", nanoid::nanoid!())?;
tree.insert("g", nanoid::nanoid!())?;
assert_eq!(3, tree.iter().into_iter().count());
Errors
Will return Err
if an IO error occurs.
sourcepub fn range<K: AsRef<[u8]>, R: RangeBounds<K>>(&self, range: R) -> Range<'_>
pub fn range<K: AsRef<[u8]>, R: RangeBounds<K>>(&self, range: R) -> Range<'_>
Returns an iterator over a range of items.
Avoid using full or unbounded ranges as they may scan a lot of items (unless limited).
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
tree.insert("a", nanoid::nanoid!())?;
tree.insert("f", nanoid::nanoid!())?;
tree.insert("g", nanoid::nanoid!())?;
assert_eq!(2, tree.range("a"..="f").into_iter().count());
Errors
Will return Err
if an IO error occurs.
sourcepub fn prefix<K: AsRef<[u8]>>(&self, prefix: K) -> Prefix<'_>
pub fn prefix<K: AsRef<[u8]>>(&self, prefix: K) -> Prefix<'_>
Returns an iterator over a prefixed set of items.
Avoid using an empty prefix as it may scan a lot of items (unless limited).
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
tree.insert("a", nanoid::nanoid!())?;
tree.insert("ab", nanoid::nanoid!())?;
tree.insert("abc", nanoid::nanoid!())?;
assert_eq!(2, tree.prefix("ab").into_iter().count());
Errors
Will return Err
if an IO error occurs.
sourcepub fn first_key_value(&self) -> Result<Option<(Arc<[u8]>, Arc<[u8]>)>>
pub fn first_key_value(&self) -> Result<Option<(Arc<[u8]>, Arc<[u8]>)>>
Returns the first key-value pair in the tree. The key in this pair is the minimum key in the tree.
Examples
use lsm_tree::{Tree, Config};
let tree = Config::new(folder).open()?;
tree.insert("1", "abc")?;
tree.insert("3", "abc")?;
tree.insert("5", "abc")?;
let (key, _) = tree.first_key_value()?.expect("item should exist");
assert_eq!(&*key, "1".as_bytes());
Errors
Will return Err
if an IO error occurs.
sourcepub fn last_key_value(&self) -> Result<Option<(Arc<[u8]>, Arc<[u8]>)>>
pub fn last_key_value(&self) -> Result<Option<(Arc<[u8]>, Arc<[u8]>)>>
Returns the last key-value pair in the tree. The key in this pair is the maximum key in the tree.
Examples
use lsm_tree::{Tree, Config};
let tree = Config::new(folder).open()?;
tree.insert("1", "abc")?;
tree.insert("3", "abc")?;
tree.insert("5", "abc")?;
let (key, _) = tree.last_key_value()?.expect("item should exist");
assert_eq!(&*key, "5".as_bytes());
Errors
Will return Err
if an IO error occurs.
sourcepub fn compare_and_swap<K: AsRef<[u8]>>(
&self,
key: K,
expected: Option<&Arc<[u8]>>,
next: Option<&Arc<[u8]>>
) -> Result<Result<(), CompareAndSwapError>>
pub fn compare_and_swap<K: AsRef<[u8]>>( &self, key: K, expected: Option<&Arc<[u8]>>, next: Option<&Arc<[u8]>> ) -> Result<Result<(), CompareAndSwapError>>
sourcepub fn fetch_update<K: AsRef<[u8]>, V: AsRef<[u8]>, F: Fn(Option<&Arc<[u8]>>) -> Option<V>>(
&self,
key: K,
f: F
) -> Result<Option<Arc<[u8]>>>
pub fn fetch_update<K: AsRef<[u8]>, V: AsRef<[u8]>, F: Fn(Option<&Arc<[u8]>>) -> Option<V>>( &self, key: K, f: F ) -> Result<Option<Arc<[u8]>>>
Atomically fetches and updates an item if it exists.
Returns the previous value if the item exists.
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
tree.insert("key", "a")?;
let prev = tree.fetch_update("key".as_bytes(), |_| Some("b"))?.expect("item should exist");
assert_eq!("a".as_bytes(), &*prev);
let item = tree.get("key")?.expect("item should exist");
assert_eq!("b".as_bytes(), &*item);
let prev = tree.fetch_update("key", |_| None::<String>)?.expect("item should exist");
assert_eq!("b".as_bytes(), &*prev);
assert!(tree.is_empty()?);
Errors
Will return Err
if an IO error occurs.
sourcepub fn update_fetch<K: AsRef<[u8]>, V: AsRef<[u8]>, F: Fn(Option<&Arc<[u8]>>) -> Option<V>>(
&self,
key: K,
f: F
) -> Result<Option<Arc<[u8]>>>
pub fn update_fetch<K: AsRef<[u8]>, V: AsRef<[u8]>, F: Fn(Option<&Arc<[u8]>>) -> Option<V>>( &self, key: K, f: F ) -> Result<Option<Arc<[u8]>>>
Atomically fetches and updates an item if it exists.
Returns the updated value if the item exists.
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder).open()?;
tree.insert("key", "a")?;
let prev = tree.update_fetch("key", |_| Some("b"))?.expect("item should exist");
assert_eq!("b".as_bytes(), &*prev);
let item = tree.get("key")?.expect("item should exist");
assert_eq!("b".as_bytes(), &*item);
let prev = tree.update_fetch("key", |_| None::<String>)?;
assert_eq!(None, prev);
assert!(tree.is_empty()?);
Errors
Will return Err
if an IO error occurs.
sourcepub fn flush(&self) -> Result<()>
pub fn flush(&self) -> Result<()>
Flushes the journal to disk, making sure all written data is persisted and crash-safe.
Examples
use lsm_tree::{Config, Tree};
let tree = Config::new(folder.clone()).open()?;
tree.insert("a", nanoid::nanoid!())?;
tree.flush()?;
let tree = Config::new(folder).open()?;
let item = tree.get("a")?;
assert!(item.is_some());
Errors
Will return Err
if an IO error occurs.