Crates.io | lsmlite-rs |
lib.rs | lsmlite-rs |
version | 0.2.1 |
source | src |
created_at | 2023-07-17 12:03:40.541081 |
updated_at | 2023-12-18 10:06:18.293632 |
description | Helsing's Rust bindings for sqlite3's lsm1 extension in stand-alone fashion. |
homepage | |
repository | https://github.com/helsing-ai/lsmlite-rs |
max_upload_size | |
id | 918424 |
size | 944,920 |
Helsing's Rust bindings for sqlite3
's lsm1
extension.
lsmlite-rs
exposes sqlite3
's lsm1 extension in stand-alone fashion (without the whole sqlite3
stack). This extension is an excellent implementation of Log-structured Merge Trees and is in principle similar in spirit to RocksDB and WiredTiger (the storage engine of MongoDB). Unlike RocksDB, for example, lsm1 structures data on stable storage as a collection of read-only B-trees (called "segments" in lsm1
's terminology) that increase in size as the database grows. Thus, lsm1
follows the fundamental design principles of bLSM rather than those of a traditional LSM-Tree - in which data is stored in immutable sorted arrays. This comes with the advantage of offering excellent I/O for reads out-of-the-box; while also being efficient at writing data.
Other appealing characteristics of lsm1
are:
src/lsm1/lsm.c
.WORM
) workloads. A database becomes essentially a single densely-packed B
-tree. Improving the space required by the database on disk, as well as providing optimal I/O for reads.lsm1
's default durability setting.Relevant work in our roadmap currently includes:
lsmlite-js
).lsmlite-py
) using PyO3. We are aware of existing python bindings for lsm1
like python-lsm-db, so this has low priority at the moment.lsm1
versionslsmlite-rs
version 0.1.0
is based on lsm1
contained in sqlite3-3.41.2 released on 22nd of March 2023. Amalgamated lsm1
file can be found under src/lsm1/lsm1-ae2e7fc.c
along a short explanation about how this file was produced and how can it be locally updated from the official SQLite3's source code.The following is a short example on how to declare and open a database, then insert data and traverse the whole database extracting all keys and values currently contained therein. Additional examples on particular topics (e.g., transactions, or compression) can be found under the examples
directory.
use lsmlite_rs::*;
// Make sure that `/tmp/my_db.lsm` does not exist yet.
let db_conf = DbConf::new("/tmp/", "my_db".to_string());
// Let's declare an empty handle.
let mut db: LsmDb = Default::default();
// Let's initialize the handle with our configuration.
let rc = db.initialize(db_conf);
// Let's connect to the database. It is at this point that the file is produced.
let rc = db.connect();
// Insert data into the database, so that something gets traversed.
// Let's persist numbers 1 to 100 with 1 KB zeroed payload.
let value = vec![0; 1024];
let max_n: usize = 100;
for n in 1..=max_n {
let key_serial = n.to_be_bytes();
let rc = db.persist(&key_serial, &value).unwrap();
}
// Let's open the cursor once we have written data (snapshot isolation).
let mut cursor = db.cursor_open().unwrap();
// Let's move the cursor to the very first record on the database.
let rc = cursor.first();
assert!(rc.is_ok());
// Now let's traverse the database extracting the data we just added.
let mut num_records = 0;
while cursor.valid().is_ok() {
num_records += 1;
// Extract the key.
let current_key = Cursor::get_key(&cursor).unwrap();
// Parse it to an integer.
assert!(current_key.len() == 8);
let key = usize::from_be_bytes(current_key.try_into().unwrap());
// Extract the value.
let current_value = Cursor::get_value(&cursor).unwrap();
// Everything should match.
assert!(key == num_records);
assert!(current_value.len() == 1024);
// Move onto the next record.
cursor.next().unwrap();
}
// We did find what we wanted.
assert_eq!(num_records, max_n);
// EOF
assert!(cursor.valid().is_err());
We would be happy to hear your thoughts, feedback and pull requests are welcome.