SurrealKV
⚠️ Development Status: SurrealKV is currently under active development and is not feature complete. The API and implementation may change significantly between versions. Use with caution in production environments.
SurrealKV is a versioned, low-level, persistent, embedded key-value database implemented in Rust using an LSM (Log-Structured Merge) tree architecture with built-in support for time-travel queries.
Features
- ACID Compliance: Full support for Atomicity, Consistency, Isolation, and Durability
- Snapshot Isolation: MVCC support with non-blocking concurrent reads and writes
- Durability Levels: Immediate and Eventual durability modes
- Time-Travel Queries: Built-in versioning with point-in-time reads and historical queries
- Checkpoint and Restore: Create consistent snapshots for backup and recovery
- Value Log (Wisckey): Ability to store large values separately, with garbage collection
Quick Start
use ;
async
Configuration
SurrealKV can be configured through various options when creating a new LSM tree:
Basic Configuration
use TreeBuilder;
let tree = new
.with_path // Database directory path
.with_max_memtable_size // 100MB memtable size
.with_block_size // 4KB block size
.with_level_count // Number of levels in LSM tree
.build?;
Options:
with_path()- Database directory where SSTables and WAL files are storedwith_max_memtable_size()- Size threshold for memtable before flushing to SSTablewith_block_size()- Size of data blocks in SSTables (affects read performance)with_level_count()- Number of levels in the LSM tree structure
Value Log Configuration
The Value Log (VLog) separates large values from the LSM tree for more efficient storage and compaction.
let tree = new
.with_path
.with_enable_vlog // Enable VLog
.with_vlog_max_file_size // 256MB VLog file size
.with_vlog_gc_discard_ratio // Trigger GC at 50% garbage
.with_vlog_checksum_verification
.build?;
Options:
with_enable_vlog()- Enable/disable Value Log for large value storagewith_vlog_max_file_size()- Maximum size of VLog files before rotationwith_vlog_gc_discard_ratio()- Threshold (0.0-1.0) for triggering VLog garbage collectionwith_vlog_checksum_verification()- Checksum verification level (DisabledorFull)
Versioning Configuration
Enable time-travel queries to read historical versions of your data:
use ;
let opts = new
.with_path
.with_versioning; // Enable versioning, retention_ns = 0 means no limit
let tree = with_options.build?;
Note: Versioning requires VLog to be enabled. When you call with_versioning(true, retention_ns), VLog is automatically enabled and configured appropriately.
Transaction Operations
Basic Operations
use TreeBuilder;
async
Transaction Modes
SurrealKV supports three transaction modes for different use cases:
use Mode;
// Read-write transaction (default)
let mut txn = tree.begin?;
// Read-only transaction - prevents any writes
let txn = tree.begin_with_mode?;
// Write-only transaction - optimized for writes, no reads allowed
let mut txn = tree.begin_with_mode?;
Range Operations
Range operations support efficient iteration over key ranges with optional limits:
// Range scan between keys (inclusive start, exclusive end)
let mut txn = tree.begin?;
let range: = txn.range?
.map
.collect;
// Keys-only scan (faster, doesn't fetch values when vlog is enabled)
let keys: = txn.keys?
.map
.collect;
// Range with limit
let limited: = txn.range?
.map
.collect;
// Delete a key
txn.delete?;
txn.commit.await?;
Note: Range iterators are double-ended, supporting both forward and backward iteration.
Durability Levels
Control the durability guarantees for your transactions:
use Durability;
let mut txn = tree.begin?;
// Eventual durability (default) - faster, data written to OS buffer
txn.set_durability;
// Immediate durability - slower, fsync before commit returns
txn.set_durability;
txn.set?;
txn.commit.await?;
Durability Levels:
Eventual: Commits are guaranteed to be persistent eventually. Data is written to the kernel buffer but not fsynced before returning fromcommit(). This is the default and provides the best performance.Immediate: Commits are guaranteed to be persistent as soon ascommit()returns. Data is fsynced to disk before returning. This is slower but provides the strongest durability guarantees.
Time-Travel Queries
Time-travel queries allow you to read historical versions of your data at specific points in time.
Enabling Versioning
use ;
let opts = new
.with_path
.with_versioning; // retention_ns = 0 means no retention limit
let tree = with_options.build?;
Writing Versioned Data
// Write data with explicit timestamps
let mut tx = tree.begin?;
tx.set_at_version?;
tx.commit.await?;
// Update with a new version at a later timestamp
let mut tx = tree.begin?;
tx.set_at_version?;
tx.commit.await?;
Point-in-Time Reads
Query data as it existed at a specific timestamp:
let tx = tree.begin?;
// Get value at specific timestamp
let value = tx.get_at_version?;
assert_eq!;
// Get value at later timestamp
let value = tx.get_at_version?;
assert_eq!;
// Range query at specific timestamp
let range: = tx.range_at_version?
.map
.collect;
// Keys-only query at timestamp (faster, when vlog enabled)
let keys: = tx.keys_at_version?
.map
.collect;
Retrieving All Versions
Get all historical versions of keys in a range:
let tx = tree.begin?;
let versions = tx.scan_all_versions?;
for in versions
Advanced Read Options
Use ReadOptions for fine-grained control over read operations:
use ReadOptions;
let tx = tree.begin?;
// Range query with limit and bounds
let options = new
.with_limit
.with_iterate_lower_bound
.with_iterate_upper_bound;
let results: = tx.range_with_options?
.map
.collect;
// Keys-only iteration (faster, doesn't fetch values from disk when vlog is enabled)
let options = new
.with_keys_only
.with_limit
.with_iterate_lower_bound
.with_iterate_upper_bound;
let keys: = tx.keys_with_options?
.map
.collect;
// Point-in-time read with options (requires versioning enabled)
let options = new
.with_timestamp
.with_limit
.with_iterate_lower_bound
.with_iterate_upper_bound;
let historical_data: = tx.range_with_options?
.map
.collect;
Checkpoint and Restore
Create consistent point-in-time snapshots of your database for backup and recovery.
Creating Checkpoints
let tree = new
.with_path
.build?;
// Insert some data
let mut txn = tree.begin?;
txn.set?;
txn.set?;
txn.commit.await?;
// Create checkpoint
let checkpoint_dir = "path/to/checkpoint";
let metadata = tree.create_checkpoint?;
println!;
println!;
println!;
println!;
Restoring from Checkpoint
// Restore database to checkpoint state
tree.restore_from_checkpoint?;
// Data is now restored to the checkpoint state
// Any data written after checkpoint creation is discarded
What's included in a checkpoint:
- All SSTables from all levels
- Current WAL segments
- Level manifest
- VLog directories (if VLog is enabled)
- Checkpoint metadata
Note: Restoring from a checkpoint discards any pending writes in the active memtable and returns the database to the exact state when the checkpoint was created.
Platform Compatibility
✅ Supported Platforms
- Linux (x86_64, aarch64): Full support including all features and tests
- macOS (x86_64, aarch64): Full support including all features and tests
❌ Not Supported
-
WebAssembly (WASM): Not supported due to fundamental incompatibilities:
- Requires file system access not available in WASM environments
- Write-Ahead Log (WAL) and Value Log (VLog) operations are not compatible
- System-level I/O operations are not available
-
Windows (x86_64): Basic functionality supported, but some features are limited:
- File operations are not thread safe (TODO)
- Some advanced file system operations may have reduced functionality
- Performance may be lower compared to Unix-like systems
History
SurrealKV has undergone a significant architectural evolution to address scalability challenges:
Previous Design (VART-based)
The original implementation used a versioned adaptive radix trie (VART) architecture with the following components:
- In-Memory Index: Versioned adaptive radix trie using vart for key-to-offset mappings
- Sequential Log Storage: Append-only storage divided into segments with binary record format
- Memory Limitations: The entire index had to reside in memory, limiting scalability for large datasets
Why the Change? The VART-based design had fundamental scalability limitations:
- Memory Constraint: The entire index must fit in memory, making it unsuitable for datasets larger than available RAM
- Recovery Overhead: Startup required scanning all log segments to rebuild the in-memory index
- Write Amplification: Each update created new versions, leading to memory pressure
Current Design (LSM Tree)
The new LSM (Log-Structured Merge) tree architecture provides:
- Compaction: Leveled compaction strategy for space utilization
- Better Scalability: Supports datasets much larger than available memory
This architectural change enables SurrealKV to handle larger then memory datasets.
License
Licensed under the Apache License, Version 2.0 - see the LICENSE file for details.