raft-rust
For learning only. This project is a teaching implementation of Raft consensus plus an in-process / multi-process KV demo. It is not a production-grade coordination service.
Standalone Raft implementation in Rust:
election → log replication → commit → state machine apply, persisted by BitCask; deployed as one YAML config + one raft-node process + TCP per node.
Documentation
| Language | Entry |
|---|---|
| English topics | en/README.md — numbered learning path 01–14 |
| Chinese topics | zh/README.md — numbered learning path 01–14 |
| Chinese project overview | docs/README.md — Chinese twin of this page |
Start here if you want the full story in English:
- 01 Architecture overview
- 04 Data flow end to end
- Then pick topic docs (election, replication, snapshots, …) from the English index.
Features
| Capability | Notes |
|---|---|
| Consensus | Leader election, log replication, majority commit |
| Reads | Leader linearizable reads (majority confirmation) |
| Pre-vote / CheckQuorum | Toggleable via Options; reduces disruption from partitions |
| Membership change | Joint consensus |
| Leadership transfer | ChangeMembership can remove the current leader; the old leader steps down after the Simple entry commits |
| Write dedup | WriteSession(client_id, seq) + SessionState |
| Snapshots | snapshot / InstallSnapshot / log compact_to |
| Storage | BitCask log-structured engine (data_dir/bitcask.log) |
| Network | TCP framing: WireMsg::{Raft, Client, ClientReply} |
| In-process cluster for tests | cluster::Cluster (channels + fault injection) |
Intentional limits (so the code stays teachable)
- Plain-text TCP, no TLS / auth (put TLS termination in front in production)
- Snapshots sent whole, not chunked/streamed
- No automatic graceful leader-migration RPC (removing the leader via membership change is how step-down happens)
- No production-grade ops (monitoring, live config hot-reload, …)
More Q&A: English FAQ · 中文 FAQ.
Dependencies
- Rust (edition 2024)
- Build:
cargo build --bins
Single node (fastest start)
With peers: [] in the config (or no peers), the process becomes Leader immediately on startup.
# Terminal 1: node
# Terminal 2: CLI (connect to a single address)
config/single.yaml:
node:
id: 1
listen: "127.0.0.1:7001"
data_dir: "data/single"
peers:
options:
heartbeat_interval: 2
election_timeout_min: 5
election_timeout_max: 10
max_append_entries: 100
pre_vote: true
check_quorum: true
snapshot_threshold: 1000
Data directory: data/single/bitcask.log (data/ is already git-ignored).
You can also run cargo run -- --config config/single.yaml (default-run = raft-node).
Multi-node (one config per node)
Three configs, three processes, e.g. config/node1.yaml / node2.yaml / node3.yaml.
# Start one process per terminal
# CLI: pass multiple peers; retries another node on failure/Abort
config/node1.yaml example:
node:
id: 1
listen: "127.0.0.1:7001"
data_dir: "data/node1"
peers:
-
-
options:
heartbeat_interval: 2
election_timeout_min: 5
election_timeout_max: 10
max_append_entries: 100
pre_vote: true
check_quorum: true
snapshot_threshold: 1000
Nodes 2 and 3 are the same idea: change id / listen / data_dir, and list the other two addresses in peers.
raft-cli commands
raft-cli --peers host:port[,host:port...] [--client-id UUID] <command>
| Command | Description |
|---|---|
put <key> <value> |
Session-bearing write (idempotent sequence number) |
get <key> |
Linearizable read |
scan |
Scan all KV entries |
status |
Leader, term, commit, voters, etc. |
members <id,id,...> |
Membership change (target voting set) |
Client session file
- Path:
.raft-cli-sessionin the current working directory when the CLI runs - Contents:
client_id+last_seq(incremented on everyput) - Purpose: retrying the same write after a timeout sends the same
(client_id, seq), avoiding double execution - Override the id with
--client-id <uuid>; the file is git-ignored
This is not cluster config and not node data; deleting it just makes the CLI renumber as a new client.
Config fields
| Field | Meaning |
|---|---|
node.id |
Node ID (u8, unique in the cluster) |
node.listen |
Local ip:port to listen on |
node.data_dir |
Data directory (BitCask + snapshot metadata) |
peers |
Other nodes { id, addr }; empty = single node |
options.heartbeat_interval |
Heartbeat interval (ticks) |
options.election_timeout_min/max |
Election timeout range (ticks) |
options.max_append_entries |
Max entries per Append |
options.pre_vote |
Whether to use Pre-vote |
options.check_quorum |
Leader steps down if it loses a quorum |
options.snapshot_threshold |
Compact after this many applies since last snapshot; 0 disables |
Logical time: TICK_INTERVAL = 100ms.
Tests
Coverage includes:
- In-process: election, forwarding, partition, leader kill, drop/reorder, concurrent clients
- Pre-vote / CheckQuorum
- Membership change, leader removal (leadership transfer)
- Session dedup, BitCask
- Restart recovery, restart after snapshot compaction
- True multi-process (
tests/multi_process.rsspawns child processes)
In-process demo (not a deployment path):
Embed as a library (optional)
use ;
let cluster = spawn; // in-process cluster for tests
let mut client = cluster.client;
wait_for_leader?;
client.put?;
Or use Node directly with your own transport (see src/lib.rs / src/net).
Project layout
src/
bin/raft_node.rs node process (one member of a single- or multi-node cluster)
bin/raft_cli.rs command-line client
net/ TCP + bincode
cluster/ in-process cluster (tests / example)
config.rs node YAML loading
raft/ protocol: node / log / membership / session / kv
storage/ Engine + BitCask
config/
single.yaml single node
node1.yaml … node3.yaml
tests/ unit and integration tests
examples/kv_cluster.rs in-process three-node demo
Known limitations
- Plain-text TCP, no TLS / auth (terminate TLS in front in production)
- Snapshots sent whole, not chunked/streamed
- No automatic graceful leader-migration RPC (removing the old leader via membership change is how step-down happens)
- Non-production-grade ops (monitoring, live config hot-reload, etc. not done)
License
Apache-2.0. The implementation follows the Raft paper closely, with extensions for membership change, Pre-vote, snapshots, and multi-process deployment.