1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! # lsm-db
//!
//! A log-structured merge-tree storage engine for Rust.
//!
//! An LSM engine is the write path that powers RocksDB, LevelDB, Cassandra, and
//! ScyllaDB: writes accumulate in a sorted in-memory buffer (the *memtable*);
//! when the buffer fills it is flushed to an immutable, sorted file on disk (an
//! *SSTable*); reads consult the buffer first and fall through to the file. The
//! design turns random writes into sequential disk writes, which is why it
//! underpins so many write-heavy stores.
//!
//! `lsm-db` packages that write path as a small, audited library so the storage
//! engines in the portfolio — `txn-db`, Hive DB — share one implementation
//! rather than each re-deriving it.
//!
//! ## The Tier-1 API
//!
//! The common case is five calls: open, put, get, delete, scan.
//!
//! ```
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use lsm_db::Lsm;
//!
//! // Open (or create) a database backed by a directory.
//! let dir = tempfile::tempdir()?;
//! let db = Lsm::open(dir.path())?;
//!
//! // Write and read arbitrary byte keys and values.
//! db.put(b"user:1", b"alice")?;
//! db.put(b"user:2", b"bob")?;
//! assert_eq!(db.get(b"user:1")?, Some(b"alice".to_vec()));
//!
//! // Delete masks the key.
//! db.delete(b"user:1")?;
//! assert_eq!(db.get(b"user:1")?, None);
//!
//! // Range scans walk keys in sorted order.
//! db.put(b"user:1", b"alice")?;
//! let users: Vec<_> = db.scan(b"user:".to_vec()..b"user;".to_vec())?.collect();
//! assert_eq!(users.len(), 2);
//! # Ok(())
//! # }
//! ```
//!
//! ## Tuning
//!
//! [`LsmConfig`] is the Tier-2 surface for tuning the write-buffer size. Pass it
//! to [`Lsm::open_with`]; [`Lsm::open`] uses the defaults.
//!
//! ```
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use lsm_db::{Lsm, LsmConfig};
//! let dir = tempfile::tempdir()?;
//! let db = Lsm::open_with(dir.path(), LsmConfig::new().memtable_capacity(1 << 20))?;
//! db.put(b"k", b"v")?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Grouped writes
//!
//! [`Batch`] applies several writes as one atomic group; see [`Lsm::write`].
//!
//! ## Durability
//!
//! Flushed runs are `fsync`ed and recorded in a manifest, so flushed data
//! survives reopening, and the on-disk format is frozen for the 1.x series.
//! Writes still buffered in the memtable when a process exits without
//! [`flush`](Lsm::flush)ing are durable only with the `durability` feature: it
//! logs every write to a `wal-db` write-ahead log before acknowledging it and
//! replays the log on open, so no acknowledged write is lost across a crash.
//!
//! ## Feature flags
//!
//! | Feature | Default | Description |
//! |---------|---------|-------------|
//! | `std` | yes | Standard library. The engine requires it. |
//! | `durability` | no | Crash-safe writes via a `wal-db` write-ahead log. |
//! | `bloom` | no | Bloom-filtered point lookups via `bloom-lib`. |
pub use crateBatch;
pub use crate;
pub use crateLsm;
pub use crate;
pub use crateScan;
/// The crate's common imports in one `use`.
///
/// ```
/// use lsm_db::prelude::*;
/// # fn main() -> Result<()> {
/// let dir = tempfile::tempdir().map_err(Error::from)?;
/// let db = Lsm::open(dir.path())?;
/// db.put(b"k", b"v")?;
/// # Ok(())
/// # }
/// ```