Imported from ben1009/toy-kv-engine (
AGENTS.md). Install upstream withnpx skills add ben1009/toy-kv-engine. Copyright stays with the author.
toy-kv-engine
A toy LSM-tree-based key-value storage engine written in Rust. This is an educational yet functional implementation that explores production-grade storage concepts including MVCC, WAL, multiple compaction strategies, and key-value separation (vLog).
Technology Stack
- Language: Rust (Edition 2024)
- Toolchain: Nightly (
nightly-2026-08-20), managed viarust-toolchainfile - Build Tool: Cargo + cargo-make (
Makefile.toml) - Test Runner: cargo-nextest
- Coverage: cargo-llvm-cov
Key dependencies:
crossbeam-skiplist— lock-free memtablearc-swap— lock-free state snapshot (replaces RwLock for reads)parking_lot— synchronization primitivestinyufo— block cache and vLog reader cache (lock-free S3-FIFO)bytes— zero-copy byte bufferscrc32fast— checksumsahash— bloom filter hashing (AES-NI accelerated)logforth— structured JSON logging on stderrlog— logging facade (bridged by logforth)nom— CLI parser combinatorsouroboros— self-referencing structscriterion— benchmarking
Project Structure
├── Cargo.toml # Workspace root (single member: kv-engine)
├── rust-toolchain # Pins nightly toolchain
├── rustfmt.toml # Formatting configuration
├── Makefile.toml # cargo-make tasks for dev workflow
├── .config/nextest.toml # nextest profile (retries, timeouts)
├── .typos.toml # Spell-check allowlist
├── lsan-suppressions.txt # LeakSanitizer suppressions
├── docs/
│ ├── bench-report-deleterange.md
│ ├── bench-report-vlog.md
│ ├── io-uring-bench.md
│ └── perf-profile.md
├── rfcs/
│ ├── 001-key-value-separation.md
│ ├── 002-io-uring-disk-writes.md
│ ├── 003-thread-per-core-compio.md
│ ├── 004-cache-backfill.md
│ ├── 005-mvcc.md
│ ├── 006-prefix-search.md
│ ├── 007-prefix-bloom-filter.md
│ ├── 008-prefetching.md
│ ├── 009-compaction-filter.md
│ └── 010-delete-range.md
└── kv-engine/
├── Cargo.toml
├── README.md
├── benches/
│ ├── deleterange_benchmarks.rs
│ ├── vlog_benchmarks.rs
│ └── vlog_index_benchmarks.rs
└── src/
├── lib.rs # Module declarations + test modules
├── bin/
│ ├── kv-engine-cli.rs # Interactive REPL CLI
│ ├── compaction-simulator.rs
│ ├── write-perf.rs # Benchmark binary (19 workloads)
│ └── wrapper.rs
├── block.rs # SST block format
├── block/
│ ├── builder.rs
│ └── iterator.rs
├── table.rs # SSTable format
├── table/
│ ├── builder.rs
│ ├── iterator.rs
│ └── bloom.rs
├── mem_table.rs # In-memory skip-list memtable
├── lsm_storage.rs # Core LSM engine (state, flush, get, put, scan)
├── lsm_iterator.rs # Full-LSM iterator
├── iterators.rs # Iterator trait definitions
├── iterators/
│ ├── merge_iterator.rs
│ ├── two_merge_iterator.rs
│ └── concat_iterator.rs
├── compact.rs # Compaction orchestration
├── compact/
│ ├── simple_leveled.rs
│ ├── leveled.rs
│ └── tiered.rs
├── wal.rs # Write-ahead log
├── manifest.rs # SST/vLog manifest tracking
├── range_tombstone.rs # Range tombstone primitives
├── mvcc.rs # MVCC internals
├── mvcc/
│ ├── txn.rs
│ └── watermark.rs
├── key.rs # Key types and helpers
├── vlog/ # Key-value separation (WiscKey-style)
│ ├── mod.rs
│ ├── builder.rs
│ ├── reader.rs
│ ├── gc.rs
│ └── index.rs # Per-file .vidx companion index for GC
├── cache.rs # Block cache (TinyUFO, lock-free)
├── debug.rs
└── tests/ # Integration tests
├── block.rs
├── bloom_compression.rs
├── cache_backfill.rs
├── compaction.rs
├── compaction_gc.rs
├── compaction_integration.rs
├── compaction_integration_2.rs
├── harness.rs
├── iterators.rs
├── leveled_compaction.rs
├── lsm_storage_extra.rs
├── manifest.rs
├── memtable.rs
├── merge_iterator.rs
├── mvcc_scan.rs
├── prefix_scan.rs
├── scan_flush.rs
├── simple_leveled_compaction.rs
├── sst.rs
├── tiered_compaction.rs
├── tiered_unit.rs
├── txn_serializable.rs
├── wal.rs
└── vlog_integration_tests/
├── mod.rs
├── sst_builder.rs
├── basic.rs
├── gc.rs
├── advanced.rs
├── cache.rs
└── manifest.rs
Build and Test Commands
Building
cargo build --workspace --all-features
cargo build --release --package kv-engine
Testing
The project uses cargo-nextest as the preferred test runner. Install it first:
cargo make install-nextest # or: cargo install cargo-nextest --locked
Run tests:
# Fast path (library tests only)
cargo nextest run --workspace --all-features --lib
# All tests including integration tests
cargo nextest run --workspace --all-features --all-targets
# Via cargo-make
cargo make test
Coverage:
cargo make test-cov # Generates HTML coverage report
Benchmarks
cargo bench --package kv-engine --bench vlog_benchmarks
The vLog benchmark compares inline vs key-value separation across write throughput, compaction time, point-get latency, and scan throughput.
All-in-one Check
cargo make check # Runs fmt, dep-sort, clippy, machete, test, typos
Individual checks:
cargo make check-fmt
cargo make check-clippy # -D warnings
cargo make check-typos
cargo make check-machete # unused dependency check
cargo make check-dep-sort # cargo-sort
Code Style Guidelines
Formatting is governed by rustfmt.toml:
- Edition / style edition: 2024
tab_spaces = 4comment_width = 120wrap_comments = truenormalize_comments = truereorder_imports = truereorder_impl_items = trueformat_code_in_doc_comments = trueformat_macro_bodies = trueformat_macro_matchers = true
Run cargo fmt --all before committing. CI enforces cargo fmt --check.
Dependency Management
- Add new deps to
kv-engine/Cargo.toml(the only crate in the workspace). - Keep deps sorted alphabetically (
cargo make check-dep-sortenforces this). cargo-macheteis used to detect unused dependencies.
Testing Instructions
Test Organization
- Unit tests live in the same file as the code they test (e.g.,
block.rshas#[cfg(test)]blocks). - Integration tests live under
kv-engine/src/tests/and are declared inkv-engine/src/tests.rs. - vLog integration tests are in
kv-engine/src/tests/vlog_integration_tests/(split intosst_builder.rs,basic.rs,gc.rs,advanced.rs,cache.rs,manifest.rs).
Test Configuration
.config/nextest.toml:
- Slow-timeout: 10s period, terminate after 3 retries, 3s grace period
- Retries: up to 3 with exponential backoff + jitter
- Test threads:
num-cpus
A test that arms a failpoint must be named failpoint_*. safety.yml mirrors these
tests with cargo test --lib --tests --all-features -- --skip integration --skip failpoint, and failpoints are process-global: without the name filter an armed
failpoint fails unrelated tests in that in-process parallel run. Every other job
uses nextest (process per test), so the leak is invisible in CI and only shows up
in a plain cargo test --lib.
Key Test Modules
tests::block— block encoding/decoding, iteration, and corrupt-input rejectiontests::sst— SSTable builder and iterator correctnesstests::iterators/merge_iterator— merge/concat iterator behaviortests::memtable— memtable operationstests::compaction/compaction_integration*/*compaction— compaction strategiestests::tiered_unit— TieredCompactionController unit teststests::lsm_storage_extra— LSM storage paths (cache stats, vlog stats, drain flush, GC, scans)tests::txn_serializable— serializable transaction OCC (conflict detection, write sets, commit)tests::mvcc_scan— MVCC snapshot scan correctnesstests::bloom_compression— bloom filter false-positive ratestests::cache_backfill— cache backfill on flush and compactiontests::harness— shared test utilities
Security Considerations
CI Security
- Workflows use
step-security/harden-runnerwith egress-policy audit (intended to becomeblockafter validation). - Actions are pinned to specific commit SHAs.
- OSSF Scorecard workflow runs on a weekly schedule.
- Dependency review workflow runs on PRs.
Sanitizers
The safety.yml workflow runs tests under:
- AddressSanitizer (
-Z sanitizer=address) - LeakSanitizer (
-Z sanitizer=leak) withlsan-suppressions.txt
Note: Miri is disabled because crossbeam-skiplist uses epoch-based GC incompatible with Miri.
Runtime Safety
- The engine uses
crossbeam-epochfor lock-free data structures. arc-swapprovides lock-free atomic state snapshots for reads.parking_lotis used for mutexes/rwlocks instead ofstd::sync.- CRC32 checksums protect SST blocks and vLog entries.
- vLog entries include key validation on read to detect stale/corrupted pointers.
Architecture Notes
LSM Storage State
LsmStorageState (in lsm_storage.rs) is the central mutable state:
memtable— current writable memtable (crossbeam SkipMap)imm_memtables— frozen memtables awaiting flushl0_sstables— L0 SST file IDslevels— L1+ tiers or levelssstables— map of SST ID →Arc<SsTable>
State mutations follow a copy-on-write pattern: the state is behind ArcSwap<LsmStorageState> so readers get lock-free snapshots via atomic load. Background tasks (flush, compaction) produce new state versions under a state_lock mutex. An active_memtable_lock: RwLock<()> prevents write-loss during memtable freeze.
Key-Value Separation (vLog)
Large values can be stored in separate .vlog files instead of inline in SSTs. This is inspired by WiscKey and reduces compaction write amplification.
Key structs:
ValuePointer— 16-byte reference(file_id, offset, size)stored in the LSM treeValueLog— manages vLog files, reference tracking, and reader cachingValueLogBuilder— writes vLog entries during SST constructionGarbageCollector— reclaims stale vLog space post-compaction
Enable via LsmStorageOptions::value_separation.
Compaction Strategies
CompactionOptions supports four strategies:
NoCompaction— disables background compactionSimpleLeveledCompaction— simple size-ratio basedLeveledCompaction— standard leveled compactionTieredCompaction— tiered/leveling hybrid
MVCC
mvcc.rs provides multi-version concurrency control with:
- Timestamp allocation and watermark tracking
- Serializable transaction support (optional, gated by
serializableoption) TransactionAPI inmvcc/txn.rs
WAL
Write-ahead logging is optional (enable_wal: bool). When enabled, each memtable has an associated WAL file for crash recovery.
Block Cache
cache.rs implements a lock-free block cache using Cloudflare's TinyUFO (S3-FIFO + TinyLFU). Configurable capacity via block_cache_capacity (default 8192 blocks, ~32MB with 4KB blocks). Per-key single-flight coalesces concurrent cache-miss I/O for the same block.
Cache Backfill
On flush and compaction, newly produced SST blocks are inserted into the block cache via force_put (bypasses TinyUFO admission). Flush backfill captures blocks from SsTableBuilder in-memory (zero extra I/O). Compaction backfill covers L0/L1/L2 tiered compactions. See RFC 004.
vLog Index
vlog/index.rs maintains per-file .vidx companion files that map keys to their vLog entry locations. This optimizes GC liveness analysis by avoiding full header scans. Indices are loaded lazily on first GC access and persisted after each flush.
Development Workflow
- Install the nightly toolchain (the
rust-toolchainfile handles this automatically). - Make changes.
- Run
cargo make checklocally to verify fmt, clippy, tests, and typos. - Open a PR. CI runs
check.yml,test.yml, andsafety.yml.
Useful Binaries
-
kv-engine-cli— interactive REPL for manual testing:cargo run --bin kv-engine-cli -- --path /tmp/lsm.db --compaction leveledSupports commands:
fill <begin> <end>,get <key>,del <key>,scan [begin] [end],dump,flush,full_compaction,quit. -
compaction-simulator— compaction strategy simulation