Embedded Log-Structured Merge-Tree (LSM) Storage Engine with MemTable, WAL, Bloom Filter SSTables & Leveled Compaction
Traditional B-Tree and relational storage engines incur catastrophic random-write penalties when handling write-intensive, streaming, or append-heavy workloads: updating balanced page nodes requires in-place disk overwrites, random seek latency, and severe write amplification.
HyperQuery-LSM is an industrial-grade, write-optimized Log-Structured Merge-Tree storage engine written in Python 3.13. It converts random write patterns into high-throughput sequential appends using an in-memory probabilistic SkipList MemTable backed by a Binary Write-Ahead Log (WAL) with CRC32 frame checksums. Immutable disk segments (SSTables) feature 60-byte binary footers, sparse index blocks for
WRITE PATH (put / delete)
|
v
+---------------------------------------------------------------------------------------+
| LSMTreeEngine |
| |
| +------------------------------+ +------------------------------------+ |
| | Write-Ahead Log | | SkipList MemTable | |
| |------------------------------| |------------------------------------| |
| | Sequential Disk Append | | Probabilistic Ordered SkipList | |
| | CRC32 Frame Integrity | ========> | O(log N) Inserts & Range Scans | |
| | Crash Recovery Replay | | Max Size / Bytes Trigger Flush | |
| +------------------------------+ +------------------------------------+ |
| | |
+------------------------------------------------------------------|--------------------+
| MemTable Full
v
[Flush to Level 0 SSTable]
|
v
+---------------------------------------------------------------------------------------+
| Level 0 SSTables |
| |
| +---------------------------------------------------------------------------------+ |
| | SSTable File Format | |
| | | |
| | [ Data Blocks: Key-Value Frames with Monotonic Timestamps & Tombstones ] | |
| | [ Sparse Index: Block Starting Keys -> Byte Offsets (Binary Search) ] | |
| | [ Bloom Filter: Kirsch-Mitzenmacher Double-Hashing Bit Array ] | |
| | [ Footer: Fixed 60-Byte Trailer with Offsets, Lengths, & 'HYPERLSM' Magic ] | |
| +---------------------------------------------------------------------------------+ |
+---------------------------------------------------------------------------------------+
| |
| Multi-Way K-Merge Compaction | Negative Query
v v
+------------------------------------------------------+ [ Bloom Rejection: 0 Seek ]
| Level 1+ SSTables |
| - Purged Tombstones (Garbage Collected) |
| - Deduplicated Keys (Highest Timestamp Wins) |
| - Bounded Read Amplification (R_A) |
+------------------------------------------------------+
For a maximum tower height
The expected height of the structure containing
This guarantees expected
To minimize false positives for
We employ Kirsch-Mitzenmacher double-hashing using two independent 32-bit seeds (
Zero False Negatives Invariant:
In a multi-level hierarchy with compaction factor
-
Write Amplification (
$W_A$ ): Bounded by$\mathcal{O}(T \cdot L)$ where$L$ is the number of active tree levels. -
Point Read Amplification (
$R_A$ ): With Bloom filter false positive rate$p \approx 0.01$ , the expected disk seeks for a point lookup:
Benchmarked on Python 3.13 standard runtime across 50,000 operations:
| Benchmark Phase | Operations / Sec | Mean Latency | Technical Guarantee |
|---|---|---|---|
| SkipList In-Memory Insert | 90,959.2 inserts/sec | 10.994 $\mu$s |
|
| SkipList In-Memory Lookup | 92,598.4 lookups/sec | 10.799 $\mu$s | Direct memory pointer traversal |
| Bloom Filter Double-Hashing | 103,155.3 checks/sec | 9.694 $\mu$s | Zero false negatives |
| SSTable Read (Existing Keys) | 31,991.5 lookups/sec | 31.258 $\mu$s | Sparse index block search |
| SSTable Read (Bloom Bypass) | 156,141.0 lookups/sec | 6.404 $\mu$s | 100% negative lookup disk bypass |
| Full Engine Write (WAL + MemTable) | 20,032.9 writes/sec | 49.918 $\mu$s | Strict WAL crash durability |
HyperQuery-LSM/
├── hyperquery/
│ ├── __init__.py # Package exports and module definitions
│ ├── types.py # Entry, Tombstone, SSTableMetadata, CompactionStats
│ ├── bloom.py # Kirsch-Mitzenmacher double-hashing Bloom Filter
│ ├── skiplist.py # Probabilistic ordered SkipList MemTable
│ ├── wal.py # Binary Write-Ahead Log with CRC32 recovery
│ ├── sstable.py # Immutable SSTables with Sparse Index & 60B Footer
│ ├── compaction.py # Multi-way leveled merge & tombstone GC
│ ├── engine.py # Unified LSMTreeEngine coordinator
│ └── cli.py # Benchmark suite and CLI tool
├── tests/
│ ├── test_bloom.py # Set membership, false positive bounds, serialization
│ ├── test_skiplist.py # Ordered operations, in-place updates, range scans
│ ├── test_wal.py # CRC32 verification and crash replay recovery
│ ├── test_sstable.py # Sparse index binary search and Bloom bypass
│ ├── test_compaction.py # Multi-way merge, deduplication, tombstone purging
│ └── test_engine.py # End-to-end put/get/delete, auto-flush, leveled query
├── pyproject.toml # Build metadata and tool configuration
├── requirements.txt # Development dependencies (pytest)
├── LICENSE # Apache-2.0 Open Source License
└── README.md # System specification and documentation
- Python 3.11, 3.12, or 3.13
- Virtual environment (recommended)
git clone https://github.com/Huighlet/HyperQuery-LSM.git
cd HyperQuery-LSM
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # Or on Windows: .venv\Scripts\activate
# Install in editable mode
pip install -e .pytest -vOutput:
tests/test_bloom.py::test_bloom_filter_zero_false_negatives PASSED [ 5%]
tests/test_bloom.py::test_bloom_filter_false_positive_bound PASSED [ 10%]
tests/test_bloom.py::test_bloom_serialization_roundtrip PASSED [ 15%]
tests/test_compaction.py::test_compaction_multiway_merge_and_deduplication PASSED [ 21%]
tests/test_engine.py::test_engine_put_get_delete PASSED [ 26%]
tests/test_engine.py::test_engine_automatic_flush_to_sstable PASSED [ 31%]
tests/test_engine.py::test_engine_range_scan PASSED [ 36%]
tests/test_engine.py::test_engine_compaction PASSED [ 42%]
tests/test_server.py::test_resp_parser_serialization PASSED [ 47%]
tests/test_server.py::test_resp_parser_request PASSED [ 52%]
tests/test_server.py::test_hyperquery_server_commands PASSED [ 57%]
tests/test_skiplist.py::test_skiplist_insert_and_get PASSED [ 63%]
tests/test_skiplist.py::test_skiplist_update_in_place PASSED [ 68%]
tests/test_skiplist.py::test_skiplist_sorted_order_and_scan PASSED [ 73%]
tests/test_skiplist.py::test_skiplist_tombstone PASSED [ 78%]
tests/test_sstable.py::test_sstable_write_and_read PASSED [ 84%]
tests/test_sstable.py::test_sstable_scan_range PASSED [ 89%]
tests/test_wal.py::test_wal_append_and_replay PASSED [ 94%]
tests/test_wal.py::test_wal_corrupted_frame_detected PASSED [100%]
============================= 19 passed in 0.48s ==============================
Run HyperQuery as a standalone TCP server speaking the standard Redis protocol (RESP2):
hyperquery server --host 0.0.0.0 --port 6379 --data ./lsm_dataConnect instantly with redis-cli or any language client:
# Connect using standard redis-cli
redis-cli -p 6379
127.0.0.1:6379> PING
PONG
127.0.0.1:6379> SET user:1001 '{"name":"Kwame","role":"Dev"}'
OK
127.0.0.1:6379> GET user:1001
"{\"name\":\"Kwame\",\"role\":\"Dev\"}"
127.0.0.1:6379> INFO
# HyperQuery-LSM Storage Engine
engine:hyperquery-lsm
memtable_entries:1
memtable_bytes:54
level0_tables:0
level1_tables:0
127.0.0.1:6379> DEL user:1001
(integer) 1
127.0.0.1:6379> GET user:1001
(nil)Deploy HyperQuery-LSM with Docker in one command:
docker build -t hyperquery-lsm .
docker run -d -p 6379:6379 -v hyperquery_data:/data --name lsm_db hyperquery-lsmhyperquery bench -n 50000from hyperquery import LSMTreeEngine
# 1. Initialize Engine with automatic flushing threshold
engine = LSMTreeEngine(
data_dir="./lsm_data",
memtable_max_entries=1000,
level0_compaction_threshold=4,
)
# 2. High-Throughput Write Operations (WAL + MemTable)
engine.put(b"user:001", b'{"name": "Kwame", "balance": 1500.0}')
engine.put(b"user:002", b'{"name": "Ama", "balance": 2800.0}')
# 3. Fast Point Lookups (MemTable -> L0 Bloom Filter -> Sparse Index)
val = engine.get(b"user:001")
print("User 001:", val.decode())
# 4. Soft Deletions (Tombstone Insertion)
engine.delete(b"user:002")
assert engine.get(b"user:002") is None
# 5. Ordered Lexicographical Range Scan
results = engine.scan(start_key=b"user:000", end_key=b"user:999")
for k, v in results:
print(f"Key: {k.decode()}, Val: {v.decode()}")
# 6. Manual Flush & Leveled Compaction
engine.flush_memtable()
stats = engine.compact_level(level=0)
print(f"Compacted {stats.input_tables} tables into Level 1, purged {stats.tombstones_purged} tombstones")
engine.close()This project is licensed under the Apache 2.0 Open Source License. See LICENSE for details.