raftmodel/lib.rs
1//!
2//! # Raft Model
3//! `raftmodel` aims to provide the rust implementations
4//! of logic model for the raft consensus algorithm.
5//!
6//! # The big picture
7//! `raftmodel` is a crate to provide a pure logic model for the raft algorithm.
8//! It aims to strictly follow raft formal TLA+ specification ([raft.tla](https://github.com/ongardie/raft.tla/blob/master/raft.tla)).
9//!
10//! To use `raftmodel`, user interacts with raft by calling `handle_message` method in `RaftServer`. The input of `handle_message`
11//! is `RaftMessage` and the output is `Vec<RaftMessage>`.
12//! For example, a client can use the following code to add a new entry to the log
13//! of the leader and replicate it to the followers.
14//! ```ignore
15//! let response = leader.handle_message(
16//! RaftMessage::ClientRequest {
17//! dest: 1,
18//! value: "x",
19//! },
20//! );
21//! ```
22//!
23//! However, this crate only implements the pure logic of the raft algorithm. In order for it to work in the real environment, user
24//! needs to implement the I/O in order to pass the messages between servers across the network and also store some information to the
25//! persistent storage such as disks.
26//! # Quick Start
27//! To demonstrate how to use this crate, we define the mock up `run_message` function in the following code. It is used to simulate the
28//! raft messages being passed between servers in the network which drives the log entries replicated from the leader to the followers.
29//! ```
30//! use crate::raftmodel::*;
31//! use std::collections::{VecDeque, HashSet};
32//! use std::fmt::Debug;
33//! fn run_message<T>(initial_message: RaftMessage<T>, servers: &mut Vec<RaftServer<T>>)
34//! where
35//! T: Sized + Clone + PartialEq + Eq + Debug + Default,
36//! {
37//! let mut messages = VecDeque::new();
38//! messages.push_back(initial_message);
39//! while let Some(msg) = messages.pop_front() {
40//! let dest = match msg {
41//! RaftMessage::ClientRequest { dest, .. }
42//! | RaftMessage::BecomeLeader { dest, .. }
43//! | RaftMessage::AppendEntries { dest, .. }
44//! | RaftMessage::AppendEntriesRequest { dest, .. }
45//! | RaftMessage::AppendEntriesResponse { dest, .. }
46//! | RaftMessage::RequestVoteRequest { dest, .. }
47//! | RaftMessage::RequestVoteResponse { dest, .. }
48//! | RaftMessage::TimeOut { dest, .. } => dest,
49//! };
50//! let server = &mut servers[dest as usize];
51//! let responses = server.handle_message(msg);
52//! messages.append(&mut responses.into_iter().collect());
53//! }
54//! }
55//!
56//!
57//!
58//! let log = create_empty_log();
59//!
60//! let mut servers = vec![
61//! RaftServer::new(log.clone()),
62//! RaftServer::new(log.clone()),
63//! RaftServer::new(log.clone()),
64//! RaftServer::new(log.clone()),
65//! RaftServer::new(log.clone()),
66//! RaftServer::new(log.clone()),
67//! ];
68//!
69//! // Let server 1 time out to become a candidate. It should win the election with all votes
70//! run_message(
71//! RaftMessage::TimeOut {
72//! dest: 1,
73//! followers: (2..6).collect(),
74//! },
75//! &mut servers,
76//! );
77//! assert_eq!(*servers[1].server_state(), ServerState::Leader);
78//!
79//! // Client append a new entry to the leader's log
80//! run_message(
81//! RaftMessage::ClientRequest{
82//! dest:1,
83//! value: "x".to_string(),
84//! },
85//! &mut servers,
86//! );
87//!
88//! // The first AppendEntries will update leader commit_index
89//! run_message(
90//! RaftMessage::AppendEntries {
91//! dest: 1,
92//! followers: (2..6).collect(),
93//! },
94//! &mut servers,
95//! );
96//!
97//! // The second AppendEntries will update all followers commit_index
98//! run_message(
99//! RaftMessage::AppendEntries {
100//! dest: 1,
101//! followers: (2..6).collect(),
102//! },
103//! &mut servers,
104//! );
105//!
106//! assert!(servers.iter().skip(1).all(|x| { servers[1].log() == x.log() }));
107//!
108//!
109//! ```
110
111mod raftmodel;
112
113pub use crate::raftmodel::*;