raft-io 1.0.0

Raft consensus and replicated-log engine for Rust. Leader election, log replication, membership changes, and snapshotting over a pluggable transport and a pluggable log store. The consensus layer above wal-db and the coordination substrate for Hive DB clustering.
Documentation
//! # raft-io
//!
//! A from-scratch implementation of the [Raft consensus algorithm], built as a
//! clean, embeddable library rather than a framework.
//!
//! The protocol core is a **deterministic state machine**: you feed it
//! [`Event`]s (logical ticks, inbound [`Message`]s, client proposals) and it
//! returns [`Action`]s (send these messages, apply this committed command).
//! Time, networking, and storage are *your* concern, injected through the
//! [`RaftLog`] and [`RaftTransport`] trait seams. That separation is exactly
//! what makes the consensus core provable: it contains no wall clock and no
//! I/O, so an entire cluster's behaviour can be reproduced from a seed and a
//! sequence of events.
//!
//! ## Status
//!
//! **Stable (`1.0`).** The protocol is complete: leader election with
//! [pre-vote] disruption protection, log replication, durable crash recovery
//! (the `persistence` feature), snapshots with log compaction, single-server
//! membership changes with non-voting [learners]
//! ([`Event::AddLearner`] / [`Event::PromoteLearner`]), leadership transfer, and
//! **linearizable reads** ([`Event::Read`] → [`Action::ReadReady`], the ReadIndex
//! protocol). All five Raft safety properties are asserted continuously by a
//! kitchen-sink adversarial test suite under combined partitions, message
//! loss/reorder/duplication, membership churn, and snapshotting; an
//! application-level suite drives a replicated key-value store to convergence —
//! and serves stale-free linearizable reads — under the same faults; and the
//! decode path is fuzzed. The public API, the wire format, and the durable log
//! format are **frozen** and will not change incompatibly before `2.0` (see the
//! normative `docs/PROTOCOL.md`). The full surface is documented in
//! `docs/API.md`, with performance baselines in `docs/BENCHMARKS.md`.
//!
//! [pre-vote]: PreVote
//! [learners]: RaftNode::learners
//!
//! ## The three tiers
//!
//! - **Tier 1** — the common case in a handful of calls, no builder and no
//!   generic to name: [`RaftNode::new`] with a [`RaftConfig`] and the default
//!   in-memory [`MemoryLog`].
//! - **Tier 2** — [`RaftConfig`]'s builder for tuning election and heartbeat
//!   timing.
//! - **Tier 3** — the [`RaftLog`] / [`RaftTransport`] traits for plugging in a
//!   durable store or a real transport.
//!
//! ## Example — a single-node cluster elects itself and commits
//!
//! ```
//! use raft_io::{Action, Event, RaftConfig, RaftNode};
//!
//! // One node, no peers: it reaches quorum (itself) the moment it times out.
//! let mut node = RaftNode::new(RaftConfig::single(1));
//!
//! // Drive logical ticks until the node becomes leader.
//! while !node.is_leader() {
//!     let _ = node.step(Event::Tick).expect("tick never fails in memory");
//! }
//! assert_eq!(node.leader(), Some(1));
//!
//! // A leader commits its own proposals immediately (quorum of one).
//! let actions = node.step(Event::Propose(b"set x = 1".to_vec())).unwrap();
//! assert!(actions.iter().any(|a| matches!(a, Action::Apply { .. })));
//! assert_eq!(node.commit_index(), 1);
//! ```
//!
//! [Raft consensus algorithm]: https://raft.github.io/

#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![deny(unused_must_use)]
#![deny(unused_results)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::expect_used)]
#![deny(clippy::todo)]
#![deny(clippy::unimplemented)]
#![deny(clippy::print_stdout)]
#![deny(clippy::print_stderr)]
#![deny(clippy::dbg_macro)]
#![cfg_attr(docsrs, feature(doc_cfg))]

mod config;
mod error;
#[cfg(feature = "framing")]
#[cfg_attr(docsrs, doc(cfg(feature = "framing")))]
pub mod framing;
mod log;
mod message;
mod node;
mod rng;
mod transport;
mod types;
#[cfg(feature = "persistence")]
mod wal_log;

pub use crate::config::RaftConfig;
pub use crate::error::{Error, Result};
pub use crate::log::{MemoryLog, RaftLog};
pub use crate::message::{
    AppendEntries, AppendEntriesReply, InstallSnapshot, InstallSnapshotReply, Message, PreVote,
    PreVoteReply, ReadProbe, ReadProbeReply, RequestVote, RequestVoteReply, TimeoutNow,
};
pub use crate::node::{Action, Event, RaftNode};
pub use crate::transport::{MemoryTransport, RaftTransport};
pub use crate::types::{EntryKind, HardState, Index, LogEntry, NodeId, Role, Snapshot, Term};
#[cfg(feature = "persistence")]
#[cfg_attr(docsrs, doc(cfg(feature = "persistence")))]
pub use crate::wal_log::WalLog;

/// The everyday surface, for `use raft_io::prelude::*;`.
///
/// This gathers the types an application touches while driving a node — the node
/// and its config, the [`Event`]/[`Action`] vocabulary, the error type, and the
/// log and transport seams with their in-memory implementations. The message and
/// other value types are available from the crate root when needed (for example
/// when implementing a transport or inspecting a [`LogEntry`]).
///
/// # Examples
///
/// ```
/// use raft_io::prelude::*;
///
/// let mut node = RaftNode::new(RaftConfig::single(1));
/// while !node.is_leader() {
///     let _ = node.step(Event::Tick).unwrap();
/// }
/// assert!(node.is_leader());
/// ```
pub mod prelude {
    #[cfg(feature = "persistence")]
    pub use crate::WalLog;
    pub use crate::{
        Action, Error, Event, Index, MemoryLog, NodeId, RaftConfig, RaftLog, RaftNode,
        RaftTransport, Result, Role, Term,
    };
}