skiff
An embedded Raft consensus library backed by sled. Skiff lets you add replicated, persistent key-value storage directly to your Rust application without running a separate consensus service.
Features
- Leader election and re-election
- Log replication with majority-commit semantics
- Hard state persistence (survives restarts)
- Dynamic cluster membership (
add_server/remove_server) - Follower request forwarding (connect to any node)
- Change subscriptions (
watchprefix for streaming updates)
Not yet implemented: log compaction / snapshotting.
Installation
[]
= "0.1"
= { = "1", = ["full"] }
Note: skiff-rs requires
protocto be installed at build time. On Debian/Ubuntu:sudo apt install protobuf-compilerOn macOS:brew install protobuf
Quick start
use ;
async
Multi-node cluster
Pass existing node addresses to join_cluster when constructing additional nodes. The new node contacts a peer and registers itself via the Raft add_server RPC.
use Builder;
use Duration;
// Node 1 — bootstraps a new single-node cluster.
let node1 = new
.set_dir
.bind
.build?;
// Node 2 — joins the cluster through node 1.
let node2 = new
.set_dir
.bind
.join_cluster
.build?;
let node1_ref = node1.clone;
spawn;
// Wait for node1 to elect itself leader before node2 tries to join.
node1.wait_for_leader.await?;
spawn;
Hierarchical keys
Keys use / as a path separator, providing a simple namespace hierarchy:
client.insert.await?;
client.insert.await?;
// List all keys under "users/"
let keys = client.list_keys.await?;
// Get all top-level prefixes
let prefixes = client.get_prefixes.await?; // ["users"]
Change subscriptions
let mut sub = client.watch.await?;
loop
Shutdown
Call shutdown() before dropping a node to allow background tasks and the sled database lock to be released cleanly:
node.shutdown;
drop;
Architecture
Each Skiff node runs two background tasks:
- Cluster-join task — on startup, contacts a peer and calls
add_serverto register the node if the cluster has more than one known member. - Election manager — drives the Raft state machine: sends heartbeats as leader (every 75 ms) or fires a randomised election timeout as follower (150–300 ms).
All inter-node communication uses gRPC (via tonic). Persistent state is stored in sled named trees:
| Tree | Contents |
|---|---|
__raft_meta |
current term, voted_for, last_applied, node ID |
__raft_log |
Raft log entries (keyed by big-endian u32 index) |
base |
root-level key-value pairs |
base_<prefix> |
key-value pairs under a namespace prefix |
Status
v0.1 — suitable for experimentation and projects that can tolerate the missing features noted above. The on-disk format is not yet considered stable.