solistore 0.2.1

Embedded LSM key-value store for SoliDB: pure Rust, durable on every commit by default
Documentation
//! What point reads hand out.

use solistore::{Db, Options};

/// A small value read back owns its bytes: holding it must not keep its
/// decoded block (or the memtable's arena chunk) alive. A `multi_get` of
/// 5,000 documents otherwise pinned thousands of blocks past their eviction.
#[test]
fn a_small_value_does_not_pin_its_block_or_arena() {
    let dir = tempfile::tempdir().unwrap();
    let db = Db::open(dir.path(), Options::default()).unwrap();
    let cf = db.default_cf();
    for i in 0..200u32 {
        db.put(&cf, format!("doc:{i:05}"), format!("value {i}").repeat(10))
            .unwrap();
    }
    let big = vec![7u8; 64 * 1024];
    db.put(&cf, b"big", &big).unwrap();

    // From the memtable.
    let v = db.get(&cf, b"doc:00042").unwrap().unwrap();
    assert!(v.is_unique(), "a memtable value is copied out of its arena");

    // From a table.
    db.flush().unwrap();
    let v = db.get(&cf, b"doc:00042").unwrap().unwrap();
    assert_eq!(v, "value 42".repeat(10).as_bytes());
    assert!(v.is_unique(), "a table value is copied out of its block");
    let got = db
        .multi_get(&cf, &[&b"doc:00001"[..], &b"doc:00150"[..]])
        .unwrap();
    assert!(got.iter().all(|v| v.as_ref().unwrap().is_unique()));

    // A value that is its whole block is handed out as it is.
    let v = db.get(&cf, b"big").unwrap().unwrap();
    assert_eq!(&v[..], &big[..]);
}

/// A value larger than any block size is written, flushed, compacted and
/// read back whole. A 70 MB compressible one used to be refused on read (a
/// 64 MiB cap the writer did not share), which poisoned the database during
/// the compaction that read it.
#[test]
fn a_very_large_value_survives_flush_compaction_and_reopen() {
    let dir = tempfile::tempdir().unwrap();
    let big: Vec<u8> = (0..70u32 << 20).map(|i| (i % 251) as u8).collect();
    {
        let db = Db::open(dir.path(), Options::default()).unwrap();
        let cf = db.default_cf();
        db.put(&cf, b"a", b"before").unwrap();
        db.put(&cf, b"big", &big).unwrap();
        db.put(&cf, b"z", b"after").unwrap();
        db.flush().unwrap();
        db.compact_range(&cf, None, None).unwrap();
        assert_eq!(&db.get(&cf, b"big").unwrap().unwrap()[..], &big[..]);
    }
    let db = Db::open(dir.path(), Options::default()).unwrap();
    let cf = db.default_cf();
    assert_eq!(&db.get(&cf, b"big").unwrap().unwrap()[..], &big[..]);
    assert_eq!(db.get(&cf, b"z").unwrap().as_deref(), Some(&b"after"[..]));
}