Skip to main content

Lsm

Struct Lsm 

Source
pub struct Lsm { /* private fields */ }
Available on crate feature std only.
Expand description

A log-structured merge-tree key-value store backed by a directory on disk.

Lsm is the Tier-1 entry point: open, put, get, delete, and scan cover the whole common case. Keys and values are arbitrary byte strings; keys are ordered lexicographically.

The handle is cheap to share: every method takes &self, and the type is Send + Sync, so one engine can be wrapped in an Arc and used from many threads.

A background thread compacts runs as they accumulate. Dropping the Lsm signals that thread to stop and joins it, so no work outlives the handle.

§Durability

Flushed runs are fsynced and recorded in a manifest, so flushed data survives reopening and a crash mid-flush or mid-compaction recovers to a consistent run set. Writes still buffered in the memtable when the process exits without a flush are not yet crash-safe; write-ahead logging arrives under the durability feature in a later release. Call flush to force the buffer to disk on demand.

§Examples

let dir = tempfile::tempdir()?;
let db = lsm_db::Lsm::open(dir.path())?;

db.put(b"hello", b"world")?;
assert_eq!(db.get(b"hello")?, Some(b"world".to_vec()));

db.delete(b"hello")?;
assert_eq!(db.get(b"hello")?, None);

Implementations§

Source§

impl Lsm

Source

pub fn open(dir: impl AsRef<Path>) -> Result<Self>

Open the database in dir, creating the directory if it does not exist, using the default LsmConfig.

The run set recorded in the manifest is reopened, so flushed data is visible immediately. Temporary files and run files orphaned by a crash mid-flush or mid-compaction are reclaimed.

§Examples
let dir = tempfile::tempdir()?;
let db = lsm_db::Lsm::open(dir.path())?;
db.put(b"k", b"v")?;
Source

pub fn open_with(dir: impl AsRef<Path>, config: LsmConfig) -> Result<Self>

Open the database in dir with an explicit LsmConfig.

§Examples
use lsm_db::{Lsm, LsmConfig};
let dir = tempfile::tempdir()?;
let db = Lsm::open_with(dir.path(), LsmConfig::new().memtable_capacity(64 * 1024))?;
db.put(b"k", b"v")?;
Source

pub fn put(&self, key: impl AsRef<[u8]>, value: impl AsRef<[u8]>) -> Result<()>

Set key to value, overwriting any previous value.

§Examples
let dir = tempfile::tempdir()?;
let db = lsm_db::Lsm::open(dir.path())?;
db.put(b"key", b"value")?;
assert_eq!(db.get(b"key")?, Some(b"value".to_vec()));
Source

pub fn delete(&self, key: impl AsRef<[u8]>) -> Result<()>

Delete key. A subsequent get returns None.

Deleting a key that is not present is not an error.

§Examples
let dir = tempfile::tempdir()?;
let db = lsm_db::Lsm::open(dir.path())?;
db.put(b"key", b"value")?;
db.delete(b"key")?;
assert_eq!(db.get(b"key")?, None);
db.delete(b"never-existed")?; // not an error
Source

pub fn write(&self, batch: Batch) -> Result<()>

Apply a Batch of writes as one group.

The whole batch is applied under a single lock acquisition, so concurrent readers observe either none or all of it.

§Examples
use lsm_db::Batch;
let dir = tempfile::tempdir()?;
let db = lsm_db::Lsm::open(dir.path())?;

let mut batch = Batch::new();
batch.put(b"a", b"1");
batch.put(b"b", b"2");
batch.delete(b"c");
db.write(batch)?;

assert_eq!(db.get(b"a")?, Some(b"1".to_vec()));
Source

pub fn get(&self, key: impl AsRef<[u8]>) -> Result<Option<Vec<u8>>>

Look up key, returning its value, or None if it is absent or deleted.

§Examples
let dir = tempfile::tempdir()?;
let db = lsm_db::Lsm::open(dir.path())?;
assert_eq!(db.get(b"missing")?, None);
db.put(b"present", b"1")?;
assert_eq!(db.get(b"present")?, Some(b"1".to_vec()));
Source

pub fn scan<R>(&self, range: R) -> Result<Scan>
where R: RangeBounds<Vec<u8>>,

Iterate the live (key, value) pairs whose key falls in range, in ascending key order.

The range is taken over Vec<u8> bounds, so the usual syntaxes all work: .., a..b, a..=b, a.., ..b. The returned Scan is a consistent snapshot taken when scan is called; later writes do not affect it.

§Examples
let dir = tempfile::tempdir()?;
let db = lsm_db::Lsm::open(dir.path())?;
db.put(b"a", b"1")?;
db.put(b"b", b"2")?;
db.put(b"c", b"3")?;

let mid: Vec<_> = db.scan(b"a".to_vec()..b"c".to_vec())?.collect();
assert_eq!(mid, vec![(b"a".to_vec(), b"1".to_vec()), (b"b".to_vec(), b"2".to_vec())]);
assert_eq!(db.scan(..)?.count(), 3);
Source

pub fn flush(&self) -> Result<()>

Force the in-memory buffer to disk as a new run.

Flushing an empty buffer is a no-op. After a successful flush every previously written key is durable and will be read back on reopen.

§Examples
let dir = tempfile::tempdir()?;
let db = lsm_db::Lsm::open(dir.path())?;
db.put(b"k", b"v")?;
db.flush()?;

drop(db);
let db = lsm_db::Lsm::open(dir.path())?;
assert_eq!(db.get(b"k")?, Some(b"v".to_vec()));

Trait Implementations§

Source§

impl Debug for Lsm

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for Lsm

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl !RefUnwindSafe for Lsm

§

impl !UnwindSafe for Lsm

§

impl Freeze for Lsm

§

impl Send for Lsm

§

impl Sync for Lsm

§

impl Unpin for Lsm

§

impl UnsafeUnpin for Lsm

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<E> WithErrorCode<E> for E

Source§

fn with_code(self, code: impl Into<String>) -> CodedError<E>

Attach an error code to an error