├── .gitignore ├── Cargo.toml ├── README.md └── src ├── lib.rs ├── log_replication ├── log_replication.rs └── mod.rs ├── main.rs ├── safety ├── mod.rs └── safety.rs ├── server.rs └── server └── leader_election.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | 17 | # Added by cargo 18 | 19 | /target 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minimal-raft" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = "1.34.0" 10 | rand = "0.8.5" 11 | serde_json = "1.0.108" 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # minimal-raft 2 | a minimalistic implementation of RAFT algorithm 3 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod log_replication; 2 | mod safety; 3 | mod server; 4 | -------------------------------------------------------------------------------- /src/log_replication/log_replication.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaibhawvipul/minimal-raft/a11ace19ab0c5ca6cf23471b1ae87412f3bc80d3/src/log_replication/log_replication.rs -------------------------------------------------------------------------------- /src/log_replication/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod log_replication; -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod leader_election; 2 | mod log_replication; 3 | mod safety; 4 | mod server; 5 | 6 | use server::{Server}; 7 | use leader_election::leader_election; 8 | use log_replication::{log_replication, append_entries_args}; 9 | use crate::server::ServerMetaData; 10 | 11 | fn main() { 12 | // ask how many servers are in the cluster, default is 3 13 | let mut num_servers = 3; 14 | let mut hostname = String::from("0.0.0.0"); 15 | let mut port_vec = vec![8080, 8081, 8082]; 16 | 17 | // todo: add command line arguments to change the number of servers, hostname, and ports 18 | 19 | // create a vector of servers 20 | let mut cluster: Vec = vec![]; 21 | 22 | for i in 1..num_servers { 23 | cluster.push(Server::init(i, hostname.clone(), port_vec[i])); 24 | } 25 | 26 | // start the servers 27 | for server in cluster.iter_mut() { 28 | server.run(); 29 | } 30 | 31 | // send request to the cluster 32 | } 33 | -------------------------------------------------------------------------------- /src/safety/mod.rs: -------------------------------------------------------------------------------- 1 | mod safety; -------------------------------------------------------------------------------- /src/safety/safety.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | use crate::server::{ServerMetaData, Server}; 3 | pub fn apply_safety_rules(server: Arc>) { 4 | // Your safety logic goes here 5 | todo!("Implement safety rules") 6 | } 7 | -------------------------------------------------------------------------------- /src/server.rs: -------------------------------------------------------------------------------- 1 | mod leader_election; 2 | 3 | use std::io::{BufRead, BufReader, Read, Write}; 4 | use std::net::{TcpListener, TcpStream}; 5 | use rand::Rng; 6 | use std::thread; 7 | use std::time::Duration; 8 | 9 | #[derive(PartialEq, Clone, Debug)] 10 | pub enum ServerState { 11 | Follower, 12 | Candidate, 13 | Leader, 14 | } 15 | 16 | #[derive(Debug)] 17 | pub struct ServerMetaData { 18 | pub id: u64, 19 | pub current_term: u64, 20 | pub voted_for: Option, 21 | pub log: Vec, 22 | pub commit_index: u64, 23 | pub state: ServerState, 24 | pub election_timeout: u64, // 150-300ms 25 | pub hostname: String, 26 | pub port: u64, 27 | pub listener: TcpListener, 28 | } 29 | 30 | // define Message enum 31 | #[derive(Clone, Debug)] 32 | pub enum Message { 33 | AppendEntries(AppendEntriesArgs), 34 | RequestVote(RequestVoteArgs), 35 | } 36 | 37 | // define AppendEntriesArgs 38 | #[derive(Clone, Debug)] 39 | pub struct AppendEntriesArgs { 40 | pub term: u64, 41 | pub leader_id: u64, 42 | pub prev_log_index: u64, 43 | pub entries: Vec, 44 | pub leader_commit: u64, 45 | } 46 | 47 | // define RequestVoteArgs struct 48 | #[derive(Clone, Debug)] 49 | pub struct RequestVoteArgs { 50 | pub term: u64, 51 | pub candidate_id: u64, 52 | pub last_log_index: u64, 53 | } 54 | 55 | // server trait 56 | pub trait Server { 57 | fn init(id: u64, hostname: String, port: u64) -> Self; 58 | 59 | fn run(&mut self, cluster: Vec) -> Self; 60 | 61 | fn run_election_timeout(&mut self, cluster: Vec) -> Self; 62 | 63 | fn send_heartbeat(&mut self) -> Self; 64 | 65 | fn start_leader_election(&mut self, cluster: Vec) -> Self; 66 | 67 | fn send_append_entries(&mut self, x: Box) -> Self; 68 | 69 | fn send_request_vote(&mut self, request_vote_args: RequestVoteArgs) -> Self; 70 | 71 | fn handle_append_entries(&mut self, cluster: Vec); 72 | 73 | fn handle_request_vote(&mut self) -> Self; 74 | } 75 | 76 | impl Server for ServerMetaData { 77 | fn init(id: u64, hostname: String, port: u64) -> Self { 78 | ServerMetaData { 79 | id, 80 | current_term: 0, 81 | voted_for: None, 82 | log: vec![], 83 | commit_index: 0, 84 | state: ServerState::Follower, 85 | election_timeout: 0, 86 | hostname: hostname.clone(), 87 | port, 88 | listener: TcpListener::bind(format!("{}:{}", hostname.clone(), port)).unwrap(), 89 | } 90 | } 91 | 92 | // run will call a function from leader 93 | fn run(&mut self, cluster: Vec) -> Self { 94 | 95 | self.run_election_timeout(cluster); 96 | 97 | if self.state == ServerState::Leader { 98 | // run leader function 99 | self.handle_append_entries(vec![]); 100 | 101 | } else if self.state == ServerState::Candidate { 102 | // run candidate function 103 | self.handle_request_vote(); 104 | 105 | } else { 106 | println!("Follower... Manual intervention required!"); 107 | } 108 | todo!() 109 | } 110 | 111 | fn run_election_timeout(&mut self, cluster: Vec) -> Self { 112 | 113 | // check if you are leader, if so, don't run election timeout 114 | if self.state == ServerState::Leader { 115 | return Self { 116 | id: self.id, 117 | current_term: self.current_term, 118 | voted_for: self.voted_for, 119 | log: self.log.clone(), 120 | commit_index: self.commit_index, 121 | state: self.state.clone(), 122 | election_timeout: self.election_timeout, 123 | hostname: self.hostname.clone(), 124 | port: self.port, 125 | listener: self.listener.try_clone().unwrap(), 126 | }; 127 | } 128 | 129 | // election timeout should be random between 150-300ms 130 | let election_timeout = rand::thread_rng().gen_range(150..300); 131 | self.election_timeout = election_timeout; 132 | 133 | // spawn a thread to listen to server.port, tcp 134 | thread::spawn(move || { 135 | for stream in self.listener.incoming() { 136 | let stream = stream.unwrap(); 137 | 138 | // if you receive a message, reset election timeout, else, start election 139 | if stream.bytes().next().is_some() { 140 | self.election_timeout = rand::thread_rng().gen_range(150..300); 141 | } else { 142 | self.start_leader_election(cluster.to_owned()); 143 | } 144 | 145 | } 146 | }); 147 | 148 | Self { 149 | id: self.id, 150 | current_term: self.current_term, 151 | voted_for: self.voted_for, 152 | log: self.log.clone(), 153 | commit_index: self.commit_index, 154 | state: self.state.clone(), 155 | election_timeout: self.election_timeout, 156 | hostname: self.hostname.clone(), 157 | port: self.port, 158 | listener: self.listener.try_clone().unwrap(), 159 | } 160 | } 161 | 162 | fn send_heartbeat(&mut self) -> Self { 163 | todo!() 164 | } 165 | 166 | fn start_leader_election(&mut self, mut cluster: Vec) -> Self { 167 | 168 | if self.state != ServerState::Leader { 169 | return Self { 170 | id: self.id, 171 | current_term: self.current_term, 172 | voted_for: self.voted_for, 173 | log: self.log.clone(), 174 | commit_index: self.commit_index, 175 | state: self.state.clone(), 176 | election_timeout: self.election_timeout, 177 | hostname: self.hostname.clone(), 178 | port: self.port, 179 | listener: self.listener.try_clone().unwrap(), 180 | }; 181 | } 182 | 183 | // change state to candidate 184 | self.state = ServerState::Candidate; 185 | 186 | // increment current term 187 | self.current_term += 1; 188 | 189 | // vote for self 190 | self.voted_for = Some(self.id); 191 | 192 | // request for votes from other servers 193 | let request_vote_args = RequestVoteArgs { 194 | term: self.current_term.clone(), 195 | candidate_id: self.id, 196 | last_log_index: self.log.len() as u64, 197 | }; 198 | 199 | // send request vote to all other servers in cluster 200 | for server in cluster.iter_mut() { 201 | if server.id == server.id { 202 | continue; 203 | } 204 | 205 | server.send_request_vote(request_vote_args.clone()); 206 | } 207 | 208 | // get votes from other servers 209 | let mut votes = 0; 210 | for server in cluster.iter_mut() { 211 | if server.id == server.id { 212 | continue; 213 | } 214 | 215 | if server.voted_for == Some(server.id) { 216 | votes += 1; 217 | } 218 | } 219 | 220 | // if votes > n/2, change state to leader 221 | if votes > cluster.len() / 2 { 222 | self.state = ServerState::Leader; 223 | // reset election timeout 224 | self.election_timeout = rand::thread_rng().gen_range(150..300); 225 | } else { 226 | // failed election 227 | self.state = ServerState::Follower; 228 | } 229 | 230 | Self { 231 | id: self.id, 232 | current_term: self.current_term, 233 | voted_for: self.voted_for, 234 | log: self.log.clone(), 235 | commit_index: self.commit_index, 236 | state: self.state.clone(), 237 | election_timeout: self.election_timeout, 238 | hostname: self.hostname.clone(), 239 | port: self.port, 240 | listener: self.listener.try_clone().unwrap(), 241 | } 242 | } 243 | 244 | fn send_append_entries(&mut self, x: Box) -> Self { 245 | todo!() 246 | } 247 | 248 | fn send_request_vote(&mut self, request_vote_args: RequestVoteArgs) -> Self { 249 | todo!() 250 | } 251 | 252 | fn handle_append_entries(&mut self, mut cluster: Vec) { 253 | // listen to server.port, tcp for append entries, don't crash if no messages, wait for election timeout 254 | for stream in self.listener.incoming() { 255 | let stream = stream.unwrap(); 256 | 257 | if stream.bytes().next().is_some() { 258 | // check if you are leader, if so then append to your log and send append log message to followers 259 | if self.state == ServerState::Leader { 260 | // append to log 261 | let mut reader = BufReader::new(stream); 262 | 263 | // only add u64 to log 264 | let mut buffer = [0; 8]; 265 | reader.read_exact(&mut buffer).unwrap(); 266 | let mut bytes = buffer.to_vec(); 267 | self.log.append(&mut bytes); 268 | 269 | // send append entries to followers 270 | let append_entries_args = AppendEntriesArgs { 271 | term: self.current_term.clone(), 272 | leader_id: self.id, 273 | prev_log_index: self.log.len() as u64, 274 | entries: self.log.clone(), 275 | leader_commit: self.commit_index, 276 | }; 277 | 278 | // send append entries to all other servers in cluster 279 | for server in cluster.iter_mut() { 280 | if server.id == server.id { 281 | continue; 282 | } 283 | 284 | server.send_append_entries(Box::new(append_entries_args.clone())); 285 | } 286 | } 287 | } 288 | } 289 | } 290 | 291 | fn handle_request_vote(&mut self) -> Self { 292 | todo!() 293 | } 294 | } -------------------------------------------------------------------------------- /src/server/leader_election.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaibhawvipul/minimal-raft/a11ace19ab0c5ca6cf23471b1ae87412f3bc80d3/src/server/leader_election.rs --------------------------------------------------------------------------------