Features
- High-throughput concurrent access using optimistic lock coupling
- Three access modes: Optimistic (non-blocking), Shared (read), Exclusive (write)
- Thread-safe (
Send + Sync) when key and value types are thread-safe - Epoch-based memory reclamation for safe concurrent access
- Bidirectional iteration with
next()andprev() - Range queries with configurable bounds
- Automatic node splitting and merging
Design
Ferntree is based on research from:
- LeanStore - Optimistic lock coupling for B-trees
- Umbra - High-performance database engine techniques
The key innovation is optimistic lock coupling: readers acquire "optimistic" access (no locks) and validate at the end that no concurrent modifications occurred. If validation fails, the operation retries automatically. This allows readers to proceed without blocking writers, and vice versa.
Quick start
use Tree;
Concurrent usage
The tree is designed for high-concurrency workloads. Wrap it in an Arc to share across threads:
use Tree;
use Arc;
use thread;
Concurrent readers and writers can operate simultaneously:
use Tree;
use Arc;
use thread;
Iterator usage
Ferntree provides several ways to iterate over entries:
use Tree;
use ;
Additional operations
use Tree;
API overview
Read operations:
lookup(key, closure)- Look up a value, applying a closure to extract dataget(key)- Get a cloned value (requiresV: Clone)contains_key(key)- Check if a key existsfirst(closure)- Get the first (minimum) entrylast(closure)- Get the last (maximum) entrylen()- Number of entriesis_empty()- Check if tree is emptyheight()- Current tree height
Write operations:
insert(key, value)- Insert or update, returns old value if key existedremove(key)- Remove an entry, returns the value if it existedpop_first()- Remove and return the first entrypop_last()- Remove and return the last entryget_or_insert(key, default)- Get existing or insert defaultget_or_insert_with(key, closure)- Get existing or insert computed valueclear()- Remove all entries
Iteration:
raw_iter()- Low-level shared iterator withseek*,next(),prev()raw_iter_mut()- Low-level exclusive iterator for modificationskeys()- Iterator over keysvalues()- Iterator over valuesrange(min, max)- Iterator over a key range
Important notes
Closure execution: The closures passed to lookup(), first(), last(), etc. may be executed multiple times if concurrent modifications cause validation failures. Avoid side effects in these closures.
// Good: Pure closure that just extracts data
let value = tree.lookup;
// Avoid: Side effects may execute multiple times
let mut counter = 0;
tree.lookup; // counter may be > 1
Thread safety: Tree<K, V> implements Send and Sync when both K and V implement Send + Sync. This means the tree can be safely shared across threads using Arc.
Memory reclamation: The tree uses epoch-based memory reclamation via crossbeam-epoch. Nodes removed from the tree are not immediately freed; they remain valid until all concurrent readers have finished. This ensures safe concurrent access without use-after-free bugs.
Original
This code is forked originally from bplustree, dual-licensed under the Apache 2.0 and MIT licenses.