Skip to content

About

Embedded Log-Structured Merge-Tree (LSM) Storage Engine with MemTable, WAL, Bloom Filter SSTables & Leveled Compaction.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

4 Commits

Folders and files

Repository files navigation

HyperQuery-LSM

Embedded Log-Structured Merge-Tree (LSM) Storage Engine with MemTable, WAL, Bloom Filter SSTables & Leveled Compaction

Build Status Python Version License Zero AI Slop


Executive Summary

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 $\mathcal{O}(\log(\text{blocks}))$ binary search addressing, and zero false-negative Bloom Filters (Kirsch-Mitzenmacher double-hashing) to eliminate unnecessary disk seeks on negative reads. Multi-way Leveled Compaction purges soft-delete tombstones, eliminates duplicate keys, and bounds read amplification ($R_A$).


System Architecture

                                      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)                  |
    +------------------------------------------------------+

Mathematical Formulations

1. Probabilistic SkipList Height Distribution

For a maximum tower height $L_{\max} = 16$ and branching probability $p = 0.5$, the probability that a node attains level $k$ is geometrically distributed:

$$P(\text{height} = k) = p^{k-1} (1 - p), \quad 1 \le k \le L_{\max}$$

The expected height of the structure containing $N$ keys:

$$\mathbb{E}[\text{height}] = \log_{1/p}(N) = \log_2(N)$$

This guarantees expected $\mathcal{O}(\log N)$ search, insertion, and point deletion complexity without the locking and rebalancing overhead of Red-Black or AVL trees.

2. Bloom Filter Optimal Parameterization

To minimize false positives for $n$ inserted keys using bit array of size $m$ and $k$ hash functions:

$$m = -\frac{n \ln p}{(\ln 2)^2} \approx -n \cdot \frac{\ln p}{0.480453}$$

$$k = \frac{m}{n} \ln 2 \approx \frac{m}{n} \cdot 0.693147$$

We employ Kirsch-Mitzenmacher double-hashing using two independent 32-bit seeds ($h_1 = \text{CRC-32}(x)$ and $h_2 = \text{FNV-1a}(x)$):

$$g_i(x) = \Big(h_1(x) + i \cdot h_2(x)\Big) \pmod m, \quad i \in [0, k-1]$$

Zero False Negatives Invariant:

$$\text{contains}(x) = \text{False} \implies x \notin \text{SSTable} \quad \text{(Zero Disk Reads Guaranteed)}$$

3. Read Amplification ($R_A$) & Write Amplification ($W_A$) Bounds

In a multi-level hierarchy with compaction factor $T = 4$:

  • 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:

$$\mathbb{E}[\text{Seeks}] = \sum_{l=0}^{L} p \approx \mathcal{O}(1)$$


Microsecond Performance Benchmarks

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 $\mathcal{O}(\log N)$ ordered ingestion
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

Directory Structure

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

Installation & Quickstart

Prerequisites

  • 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 .

Running the Test Suite

pytest -v

Output:

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 ==============================

Running Standalone Redis-Compatible TCP Server

Run HyperQuery as a standalone TCP server speaking the standard Redis protocol (RESP2):

hyperquery server --host 0.0.0.0 --port 6379 --data ./lsm_data

Connect 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)

Production Docker Deployment

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-lsm

Executing Microsecond Benchmarks

hyperquery bench -n 50000

Python API Usage

from 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()

License

This project is licensed under the Apache 2.0 Open Source License. See LICENSE for details.

About

Embedded Log-Structured Merge-Tree (LSM) Storage Engine with MemTable, WAL, Bloom Filter SSTables & Leveled Compaction.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages