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::*;