rust-raft is a proof-of-concept implementation of the Raft consensus algorithm in Rust. It focuses on learning and validating core Raft ideas—leader election, log replication scaffolding, and crash-safe persistence—rather than production readiness. This repository exists to explore how a Raft node can be modeled with async Rust and gRPC-based RPCs.
- Learning Raft by building it: the codebase is intentionally small and readable to make the core mechanics easy to follow.
- Exploring Rust + async patterns: uses Tokio for async runtime and Tonic for gRPC RPCs.
- Experimentation and iteration: designed to evolve quickly while verifying assumptions.
This is not a complete or production-ready Raft implementation. Expect incomplete features, breaking changes, and rough edges.
At a high level, each node:
- Maintains persistent state using a simple file-backed store (term + log)
- Runs an async scheduler for time-based Raft behavior
- Serves gRPC endpoints for Raft RPCs
Key modules:
src/node/*: Raft node state, RPC handlers, and schedulersrc/storage/*: Persistent storage API and implementationsrc/log/*: Log structures and helpers
- Single-node process boot with gRPC server
- Basic persistent state wiring
- Foundation for Raft scheduling and RPC wiring
- Not a full Raft implementation
- No production hardening, security, or stability guarantees
- APIs and behavior may change without notice
- Rust toolchain (stable)
cargo buildBy default the node binds on 127.0.0.1:50051.
cargo runcargo testThe binary can be configured via environment variables:
RAFT_NODE_ID: unique node identifier (default:node-1)RAFT_PEERS: comma-separated peer IDs (default: empty)RAFT_GRPC_BIND: gRPC bind address (default:127.0.0.1:50051)RAFT_TERM_FILE: term storage file (default:raft_term.dat)RAFT_LOG_FILE: log storage file (default:raft_log.dat)
Example:
RAFT_NODE_ID=node-1 \
RAFT_PEERS=node-2,node-3 \
RAFT_GRPC_BIND=127.0.0.1:50051 \
cargo runYou can embed this crate as a Raft consensus component in another Rust service. This is still a PoC, but the library provides the core building blocks:
RaftNodefor node state and transitionsNodeSchedulerfor election/heartbeat schedulingNodeRpcServicefor gRPC-based Raft RPCsPersistentStorefor basic crash-safe state persistence
Add this crate as a path dependency (or git dependency if you publish it later):
[dependencies]
rust-raft = { path = "../rust-raft" }use std::sync::Arc;
use rust_raft::{
config::RaftConfig,
node::{
node::RaftNode,
rpc::{NodeRpcService, proto::raft_rpc_server::RaftRpcServer},
scheduler::NodeScheduler,
},
storage::storage::PersistentStore,
};
use tonic::transport::Server;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = RaftConfig::from_env()?;
let term_file = tokio::fs::File::options()
.read(true)
.write(true)
.create(true)
.open(&config.term_file_path)
.await?;
let log_file = tokio::fs::File::options()
.read(true)
.write(true)
.create(true)
.open(&config.log_file_path)
.await?;
let storage = Box::new(PersistentStore::new(log_file, term_file));
let shared_node = Arc::new(tokio::sync::RwLock::new(RaftNode::new(
config.node_id,
config.peers,
storage,
)));
let scheduler = NodeScheduler::new(shared_node.clone());
tokio::spawn(async move { scheduler.start().await });
Server::builder()
.add_service(RaftRpcServer::new(NodeRpcService::new(shared_node)))
.serve(config.grpc_bind)
.await?;
Ok(())
}- There is no stable public API yet; expect breaking changes.
- Leader election is basic and not hardened.
- You will need to design your own state machine/apply layer on top.
This project is licensed under the MIT License.