armdb 0.1.12

sharded bitcask key-value storage optimized for NVMe
Documentation
#[cfg(feature = "var-collections")]
use armdb::VarTree;
use armdb::{Config, ConstTree};
use std::sync::Arc;
use std::thread;
use tempfile::tempdir;

#[test]
fn test_concurrent_writes() {
    let dir = tempdir().expect("failed to create tempdir");
    let config = Config::test();
    let tree = Arc::new(
        ConstTree::<[u8; 8], 8>::open(dir.path(), config).expect("failed to create const tree"),
    );

    let mut handles = Vec::new();

    for t in 0..4u64 {
        let tree = Arc::clone(&tree);
        let handle = thread::spawn(move || {
            let start = t * 250;
            let end = start + 250;
            for i in start..end {
                let key = i.to_be_bytes();
                let value = (i * 7).to_be_bytes();
                tree.put(&key, &value).expect("put failed");
            }
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().expect("thread panicked");
    }

    assert_eq!(tree.len(), 1000);

    for i in 0..1000u64 {
        let key = i.to_be_bytes();
        let expected = (i * 7).to_be_bytes();
        let val = tree
            .get(&key)
            .unwrap_or_else(|| panic!("key {} not found", i));
        assert_eq!(val, expected, "value mismatch for key {}", i);
    }
}

#[test]
fn test_concurrent_read_write() {
    let dir = tempdir().expect("failed to create tempdir");
    let config = Config::test();
    let tree = Arc::new(
        ConstTree::<[u8; 8], 8>::open(dir.path(), config).expect("failed to create const tree"),
    );

    // Pre-populate with keys 0..100
    for i in 0..100u64 {
        let key = i.to_be_bytes();
        let value = (i * 7).to_be_bytes();
        tree.put(&key, &value).expect("pre-populate put failed");
    }

    let mut handles = Vec::new();

    // Writer thread: writes keys 100..1000
    {
        let tree = Arc::clone(&tree);
        let handle = thread::spawn(move || {
            for i in 100..1000u64 {
                let key = i.to_be_bytes();
                let value = (i * 7).to_be_bytes();
                tree.put(&key, &value).expect("writer put failed");
            }
        });
        handles.push(handle);
    }

    // 3 reader threads
    for _ in 0..3 {
        let tree = Arc::clone(&tree);
        let handle = thread::spawn(move || {
            for iter in 0..1000u64 {
                // Derive a key from the iteration to cover the full range deterministically
                let i = iter % 1000;
                let key = i.to_be_bytes();
                if let Some(val) = tree.get(&key) {
                    let expected = (i * 7).to_be_bytes();
                    assert_eq!(val, expected, "reader got wrong value for key {}", i);
                }
                // None is fine — the writer may not have reached this key yet
            }
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().expect("thread panicked");
    }

    // Verify final state: all 1000 keys present
    assert_eq!(tree.len(), 1000);

    for i in 0..1000u64 {
        let key = i.to_be_bytes();
        let expected = (i * 7).to_be_bytes();
        let val = tree
            .get(&key)
            .unwrap_or_else(|| panic!("key {} not found after join", i));
        assert_eq!(val, expected, "final value mismatch for key {}", i);
    }
}

#[cfg(feature = "var-collections")]
#[test]
fn test_concurrent_var_writes() {
    let dir = tempdir().expect("failed to create tempdir");
    let config = Config::test();
    let tree =
        Arc::new(VarTree::<[u8; 8]>::open(dir.path(), config).expect("failed to create var tree"));

    let mut handles = Vec::new();

    for t in 0..4u64 {
        let tree = Arc::clone(&tree);
        let handle = thread::spawn(move || {
            let start = t * 250;
            let end = start + 250;
            for i in start..end {
                let key = i.to_be_bytes();
                let value = format!("val_{}", i).into_bytes();
                tree.put(&key, &value).expect("var put failed");
            }
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().expect("thread panicked");
    }

    assert_eq!(tree.len(), 1000);

    for i in 0..1000u64 {
        let key = i.to_be_bytes();
        let expected = format!("val_{}", i).into_bytes();
        let val = tree
            .get(&key)
            .unwrap_or_else(|| panic!("var key {} not found", i));
        assert_eq!(
            val.as_ref(),
            expected.as_slice(),
            "var value mismatch for key {}",
            i
        );
    }
}