fastskip
Lock-free arena-backed skip list for LSM-tree memtables. Designed for high-throughput write workloads in storage engines like RocksDB, LevelDB, and CockroachDB.
Why fastskip?
| Feature | Description |
|---|---|
| Lock-free writes | Multiple threads insert/delete concurrently via single CAS at level 0 |
| Lock-free reads | Point lookups walk the skip list without any locks |
| O(1) allocation | Per-thread arena shards — zero-contention bump allocation |
| Snapshot isolation | Point-in-time snapshots remain consistent under concurrent writes |
| Range cursors | Cursor with lower-bound seek for prefix/range iteration |
| Seal/freeze lifecycle | seal() freezes memtable for flushing, returns fresh one |
| Bulk deallocation | Dropping memtable reclaims all arena memory at once |
| Auto-seal thresholds | Automatic sealing based on memory usage or entry count |
| Backpressure detection | Detect when nearing capacity limits |
| Batch operations | Efficient bulk insert and multi-get operations |
Quick Start
Basic Operations
use ConcurrentSkipList;
let sl = new;
sl.insert;
sl.insert;
let = sl.get.unwrap;
assert_eq!;
assert!;
With Auto-Seal Thresholds
use ConcurrentSkipList;
// Create with auto-seal at 1MB memory or 100k entries
let sl = with_capacity_and_shards;
// Check if nearing limits
if !sl.is_under_backpressure
// Get memory statistics
println!;
println!;
println!;
Delete / Tombstones
Delete marks a key with a tombstone. Deleted keys remain in the skip list until flushed to SSTable.
use ConcurrentSkipList;
let sl = new;
sl.insert;
// Delete returns true if key was found
assert!;
// get still returns the key, but tombstone = true
let = sl.get.unwrap;
assert!;
// get_live filters out tombstones
assert_eq!;
LSM Memtable Lifecycle
The intended lifecycle for an LSM-tree storage engine:
use ConcurrentSkipList;
// 1. Create active memtable with auto-seal thresholds
let memtable = with_capacity_and_shards;
// 2. Concurrent writers insert/delete
memtable.insert;
memtable.delete;
// 3. Check for backpressure before inserting batch
if !memtable.is_under_backpressure
// 4. When full (auto-sealed or manual), seal it — returns frozen (for flush) + fresh (for writes)
let = memtable.seal.unwrap;
// 5. Flush frozen to SSTable (iterate snapshot)
for entry in frozen.iter
// 6. Drop frozen (reclaims arena memory)
drop;
// 7. Fresh memtable ready for new writes
fresh.insert;
// Monitor memory usage
println!;
println!;
Snapshots — Point-in-Time Iteration
Readers are fully lock-free. For consistent reads under concurrent writes, use a snapshot:
use ConcurrentSkipList;
let sl = new;
sl.insert;
sl.insert;
// Snapshot captures sequence number — skips post-snapshot inserts
let snap = sl.snapshot;
// Insert more after snapshot (won't appear in snapshot)
sl.insert;
// Snapshot sees only "a" and "b"
assert_eq!;
// Live iterator sees all three
assert_eq!;
Range Scans
use ConcurrentSkipList;
let sl = new;
sl.insert;
sl.insert;
sl.insert;
sl.insert;
// Seek to first key >= "banana"
let mut cursor = sl.cursor_at.unwrap;
while let Some = cursor.next_entry
// Output: banana, cherry, date
Memory Stats
use ConcurrentSkipList;
let sl = new;
sl.insert;
println!;
println!;
println!;
println!;
println!;
println!;
println!;
println!;
Use Cases
- LSM-tree memtables — RocksDB, LevelDB, CockroachDB style storage engines
- Write-heavy key-value stores — high-throughput ingestion pipelines
- Time-series append — sequential inserts, periodic flush to disk
- Request-scoped caches — per-request memtable, bulk reclaim on completion
See USAGE.md for complete API reference.
Configuration
use ConcurrentSkipList;
// Default: CPU count shards, 64KB initial arena per shard
let sl = new;
// Custom shard count
let sl = with_shards;
// Custom capacity and shards
let sl = with_capacity_and_shards;
// With auto-seal thresholds
let sl = with_capacity_and_shards;
Thread Safety
ConcurrentSkipList is Send + Sync — can be shared across threads:
use ConcurrentSkipList;
use Arc;
use thread;
let sl = new;
let sl1 = sl.clone;
let t1 = spawn;
let sl2 = sl.clone;
let t2 = spawn;
t1.join.unwrap;
t2.join.unwrap;
assert_eq!;
Design
| Property | Detail |
|---|---|
| Algorithm | Lock-free skip list (RocksDB InlineSkipList / CockroachDB arenaskl) |
| Concurrency | Single CAS at level 0, best-effort CAS at upper levels |
| Memory | Per-thread arena shards, no per-node free |
| Ordering | Lexicographic byte ordering |
| Duplicates | Rejected (first value wins) |
| Delete | Tombstone (entry stays, marked deleted) |
| Re-insert after delete | Seal memtable, write to fresh one |
| Auto-seal | Size or count based thresholds |
| Backpressure | 90% threshold warning |
| Batch ops | insert_batch, get_many |
Testing
Documentation
See USAGE.md for complete API reference and examples.