raft_io/lib.rs
1//! # raft-io
2//!
3//! A from-scratch implementation of the [Raft consensus algorithm], built as a
4//! clean, embeddable library rather than a framework.
5//!
6//! The protocol core is a **deterministic state machine**: you feed it
7//! [`Event`]s (logical ticks, inbound [`Message`]s, client proposals) and it
8//! returns [`Action`]s (send these messages, apply this committed command).
9//! Time, networking, and storage are *your* concern, injected through the
10//! [`RaftLog`] and [`RaftTransport`] trait seams. That separation is exactly
11//! what makes the consensus core provable: it contains no wall clock and no
12//! I/O, so an entire cluster's behaviour can be reproduced from a seed and a
13//! sequence of events.
14//!
15//! ## Status
16//!
17//! This is `v0.8`: **alpha — feature complete, hardened, in consumer
18//! integration.** The full protocol — election (with [pre-vote] disruption
19//! protection), replication, durable crash recovery (`persistence`), snapshots,
20//! membership changes, and leadership transfer — is in place and verified by a
21//! kitchen-sink adversarial test suite that asserts all five Raft safety
22//! properties under combined partitions, message loss/reorder/duplication,
23//! membership churn, and snapshotting, plus an application-level suite that
24//! drives a replicated key-value store to convergence under the same faults. The
25//! public traits and the wire and WAL formats are frozen (see `docs/PROTOCOL.md`);
26//! the decode path is fuzzed. Additions in this line stay MINOR-compatible — the
27//! pre-vote messages, for instance, are new `#[non_exhaustive]` enum variants that
28//! leave every existing wire and WAL encoding untouched. See `docs/API.md` for the
29//! full surface.
30//!
31//! [pre-vote]: PreVote
32//!
33//! ## The three tiers
34//!
35//! - **Tier 1** — the common case in a handful of calls, no builder and no
36//! generic to name: [`RaftNode::new`] with a [`RaftConfig`] and the default
37//! in-memory [`MemoryLog`].
38//! - **Tier 2** — [`RaftConfig`]'s builder for tuning election and heartbeat
39//! timing.
40//! - **Tier 3** — the [`RaftLog`] / [`RaftTransport`] traits for plugging in a
41//! durable store or a real transport.
42//!
43//! ## Example — a single-node cluster elects itself and commits
44//!
45//! ```
46//! use raft_io::{Action, Event, RaftConfig, RaftNode};
47//!
48//! // One node, no peers: it reaches quorum (itself) the moment it times out.
49//! let mut node = RaftNode::new(RaftConfig::single(1));
50//!
51//! // Drive logical ticks until the node becomes leader.
52//! while !node.is_leader() {
53//! let _ = node.step(Event::Tick).expect("tick never fails in memory");
54//! }
55//! assert_eq!(node.leader(), Some(1));
56//!
57//! // A leader commits its own proposals immediately (quorum of one).
58//! let actions = node.step(Event::Propose(b"set x = 1".to_vec())).unwrap();
59//! assert!(actions.iter().any(|a| matches!(a, Action::Apply { .. })));
60//! assert_eq!(node.commit_index(), 1);
61//! ```
62//!
63//! [Raft consensus algorithm]: https://raft.github.io/
64
65#![forbid(unsafe_code)]
66#![deny(missing_docs)]
67#![deny(unused_must_use)]
68#![deny(unused_results)]
69#![deny(clippy::unwrap_used)]
70#![deny(clippy::expect_used)]
71#![deny(clippy::todo)]
72#![deny(clippy::unimplemented)]
73#![deny(clippy::print_stdout)]
74#![deny(clippy::print_stderr)]
75#![deny(clippy::dbg_macro)]
76#![cfg_attr(docsrs, feature(doc_cfg))]
77
78mod config;
79mod error;
80#[cfg(feature = "framing")]
81#[cfg_attr(docsrs, doc(cfg(feature = "framing")))]
82pub mod framing;
83mod log;
84mod message;
85mod node;
86mod rng;
87mod transport;
88mod types;
89#[cfg(feature = "persistence")]
90mod wal_log;
91
92pub use crate::config::RaftConfig;
93pub use crate::error::{Error, Result};
94pub use crate::log::{MemoryLog, RaftLog};
95pub use crate::message::{
96 AppendEntries, AppendEntriesReply, InstallSnapshot, InstallSnapshotReply, Message, PreVote,
97 PreVoteReply, RequestVote, RequestVoteReply, TimeoutNow,
98};
99pub use crate::node::{Action, Event, RaftNode};
100pub use crate::transport::{MemoryTransport, RaftTransport};
101pub use crate::types::{EntryKind, HardState, Index, LogEntry, NodeId, Role, Snapshot, Term};
102#[cfg(feature = "persistence")]
103#[cfg_attr(docsrs, doc(cfg(feature = "persistence")))]
104pub use crate::wal_log::WalLog;
105
106/// The everyday surface, for `use raft_io::prelude::*;`.
107///
108/// This gathers the types an application touches while driving a node — the node
109/// and its config, the [`Event`]/[`Action`] vocabulary, the error type, and the
110/// log and transport seams with their in-memory implementations. The message and
111/// other value types are available from the crate root when needed (for example
112/// when implementing a transport or inspecting a [`LogEntry`]).
113///
114/// # Examples
115///
116/// ```
117/// use raft_io::prelude::*;
118///
119/// let mut node = RaftNode::new(RaftConfig::single(1));
120/// while !node.is_leader() {
121/// let _ = node.step(Event::Tick).unwrap();
122/// }
123/// assert!(node.is_leader());
124/// ```
125pub mod prelude {
126 #[cfg(feature = "persistence")]
127 pub use crate::WalLog;
128 pub use crate::{
129 Action, Error, Event, Index, MemoryLog, NodeId, RaftConfig, RaftLog, RaftNode,
130 RaftTransport, Result, Role, Term,
131 };
132}