1#![allow(dead_code)]
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
10pub struct NodeId(pub u64);
11
12impl NodeId {
13 #[must_use]
15 pub fn new(id: u64) -> Self {
16 Self(id)
17 }
18
19 #[must_use]
21 pub fn inner(self) -> u64 {
22 self.0
23 }
24}
25
26impl std::fmt::Display for NodeId {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(f, "Node({})", self.0)
29 }
30}
31
32#[derive(
34 Debug,
35 Clone,
36 Copy,
37 PartialEq,
38 Eq,
39 PartialOrd,
40 Ord,
41 serde::Serialize,
42 serde::Deserialize,
43 Default,
44)]
45pub struct RaftTerm(pub u64);
46
47impl RaftTerm {
48 #[must_use]
50 pub fn new(term: u64) -> Self {
51 Self(term)
52 }
53
54 #[must_use]
56 pub fn inner(self) -> u64 {
57 self.0
58 }
59
60 #[must_use]
62 pub fn increment(self) -> Self {
63 Self(self.0 + 1)
64 }
65}
66
67impl std::fmt::Display for RaftTerm {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 write!(f, "Term({})", self.0)
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum RaftRole {
76 Follower,
78 Candidate,
80 Leader,
82}
83
84impl RaftRole {
85 #[must_use]
87 pub fn can_vote(self) -> bool {
88 matches!(self, RaftRole::Follower | RaftRole::Candidate)
89 }
90}
91
92impl std::fmt::Display for RaftRole {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 match self {
95 RaftRole::Follower => write!(f, "Follower"),
96 RaftRole::Candidate => write!(f, "Candidate"),
97 RaftRole::Leader => write!(f, "Leader"),
98 }
99 }
100}
101
102#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
104pub struct LogEntry {
105 pub term: RaftTerm,
107 pub index: u64,
109 pub command: String,
111}
112
113impl LogEntry {
114 pub fn new(term: RaftTerm, index: u64, command: impl Into<String>) -> Self {
116 Self {
117 term,
118 index,
119 command: command.into(),
120 }
121 }
122}
123
124#[derive(Debug, Default)]
126pub struct RaftLog {
127 pub entries: Vec<LogEntry>,
129}
130
131impl RaftLog {
132 #[must_use]
134 pub fn new() -> Self {
135 Self {
136 entries: Vec::new(),
137 }
138 }
139
140 pub fn append(&mut self, entry: LogEntry) {
142 self.entries.push(entry);
143 }
144
145 #[must_use]
147 pub fn entry_at(&self, idx: u64) -> Option<&LogEntry> {
148 if idx == 0 {
149 return None;
150 }
151 self.entries.get((idx - 1) as usize)
152 }
153
154 #[must_use]
156 pub fn last_index(&self) -> u64 {
157 self.entries.len() as u64
158 }
159
160 #[must_use]
162 pub fn last_term(&self) -> RaftTerm {
163 self.entries.last().map(|e| e.term).unwrap_or_default()
164 }
165}
166
167#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
169pub struct VoteRequest {
170 pub term: RaftTerm,
172 pub candidate_id: NodeId,
174 pub last_log_index: u64,
176 pub last_log_term: RaftTerm,
178}
179
180#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
182pub struct VoteResponse {
183 pub term: RaftTerm,
185 pub vote_granted: bool,
187}
188
189#[derive(Debug)]
191pub struct RaftNode {
192 pub id: NodeId,
194 pub role: RaftRole,
196 pub current_term: RaftTerm,
198 pub voted_for: Option<NodeId>,
200 pub log: RaftLog,
202 pub commit_index: u64,
204 pub last_applied: u64,
206}
207
208impl RaftNode {
209 #[must_use]
211 pub fn new(id: NodeId) -> Self {
212 Self {
213 id,
214 role: RaftRole::Follower,
215 current_term: RaftTerm::default(),
216 voted_for: None,
217 log: RaftLog::new(),
218 commit_index: 0,
219 last_applied: 0,
220 }
221 }
222
223 pub fn process_vote_request(&mut self, req: &VoteRequest) -> VoteResponse {
230 if req.term > self.current_term {
232 self.step_down(req.term);
233 }
234
235 if req.term < self.current_term {
236 return VoteResponse {
237 term: self.current_term,
238 vote_granted: false,
239 };
240 }
241
242 let already_voted_other = self.voted_for.is_some_and(|v| v != req.candidate_id);
244
245 if already_voted_other {
246 return VoteResponse {
247 term: self.current_term,
248 vote_granted: false,
249 };
250 }
251
252 let our_last_term = self.log.last_term();
254 let our_last_index = self.log.last_index();
255
256 let log_ok = req.last_log_term > our_last_term
257 || (req.last_log_term == our_last_term && req.last_log_index >= our_last_index);
258
259 if log_ok {
260 self.voted_for = Some(req.candidate_id);
261 VoteResponse {
262 term: self.current_term,
263 vote_granted: true,
264 }
265 } else {
266 VoteResponse {
267 term: self.current_term,
268 vote_granted: false,
269 }
270 }
271 }
272
273 pub fn become_candidate(&mut self) {
275 self.current_term = self.current_term.increment();
276 self.role = RaftRole::Candidate;
277 self.voted_for = Some(self.id); }
279
280 pub fn become_leader(&mut self) {
282 self.role = RaftRole::Leader;
283 }
284
285 pub fn step_down(&mut self, new_term: RaftTerm) {
287 self.current_term = new_term;
288 self.role = RaftRole::Follower;
289 self.voted_for = None;
290 }
291}
292
293#[derive(Debug, Clone)]
295pub struct ElectionTimer {
296 pub timeout_ms: u64,
298 pub last_reset_ms: u64,
300}
301
302impl ElectionTimer {
303 #[must_use]
305 pub fn new(timeout_ms: u64, now_ms: u64) -> Self {
306 Self {
307 timeout_ms,
308 last_reset_ms: now_ms,
309 }
310 }
311
312 #[must_use]
314 pub fn is_expired(&self, now_ms: u64) -> bool {
315 now_ms.saturating_sub(self.last_reset_ms) >= self.timeout_ms
316 }
317
318 pub fn reset(&mut self, now_ms: u64) {
320 self.last_reset_ms = now_ms;
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 fn make_node(id: u64) -> RaftNode {
329 RaftNode::new(NodeId::new(id))
330 }
331
332 #[test]
333 fn test_node_initial_state() {
334 let node = make_node(1);
335 assert_eq!(node.id, NodeId::new(1));
336 assert_eq!(node.role, RaftRole::Follower);
337 assert_eq!(node.current_term, RaftTerm::default());
338 assert!(node.voted_for.is_none());
339 }
340
341 #[test]
342 fn test_raft_role_can_vote() {
343 assert!(RaftRole::Follower.can_vote());
344 assert!(RaftRole::Candidate.can_vote());
345 assert!(!RaftRole::Leader.can_vote());
346 }
347
348 #[test]
349 fn test_raft_term_increment() {
350 let term = RaftTerm::new(5);
351 assert_eq!(term.increment().inner(), 6);
352 }
353
354 #[test]
355 fn test_raft_log_append_and_entry_at() {
356 let mut log = RaftLog::new();
357 assert_eq!(log.last_index(), 0);
358 assert_eq!(log.last_term(), RaftTerm::default());
359
360 log.append(LogEntry::new(RaftTerm::new(1), 1, "cmd1"));
361 log.append(LogEntry::new(RaftTerm::new(1), 2, "cmd2"));
362
363 assert_eq!(log.last_index(), 2);
364 assert_eq!(log.last_term(), RaftTerm::new(1));
365 assert!(log.entry_at(1).is_some());
366 assert_eq!(log.entry_at(1).expect("entry should exist").command, "cmd1");
367 assert!(log.entry_at(0).is_none());
368 assert!(log.entry_at(3).is_none());
369 }
370
371 #[test]
372 fn test_become_candidate() {
373 let mut node = make_node(1);
374 node.become_candidate();
375 assert_eq!(node.role, RaftRole::Candidate);
376 assert_eq!(node.current_term, RaftTerm::new(1));
377 assert_eq!(node.voted_for, Some(NodeId::new(1)));
378 }
379
380 #[test]
381 fn test_become_leader() {
382 let mut node = make_node(1);
383 node.become_candidate();
384 node.become_leader();
385 assert_eq!(node.role, RaftRole::Leader);
386 }
387
388 #[test]
389 fn test_step_down() {
390 let mut node = make_node(1);
391 node.become_candidate();
392 node.step_down(RaftTerm::new(5));
393 assert_eq!(node.role, RaftRole::Follower);
394 assert_eq!(node.current_term, RaftTerm::new(5));
395 assert!(node.voted_for.is_none());
396 }
397
398 #[test]
399 fn test_vote_request_grant() {
400 let mut node = make_node(2);
401 let req = VoteRequest {
402 term: RaftTerm::new(1),
403 candidate_id: NodeId::new(1),
404 last_log_index: 0,
405 last_log_term: RaftTerm::default(),
406 };
407 let resp = node.process_vote_request(&req);
408 assert!(resp.vote_granted);
409 }
410
411 #[test]
412 fn test_vote_request_deny_lower_term() {
413 let mut node = make_node(2);
414 node.step_down(RaftTerm::new(3));
415 let req = VoteRequest {
416 term: RaftTerm::new(2),
417 candidate_id: NodeId::new(1),
418 last_log_index: 0,
419 last_log_term: RaftTerm::default(),
420 };
421 let resp = node.process_vote_request(&req);
422 assert!(!resp.vote_granted);
423 }
424
425 #[test]
426 fn test_vote_request_deny_already_voted() {
427 let mut node = make_node(2);
428 let req1 = VoteRequest {
429 term: RaftTerm::new(1),
430 candidate_id: NodeId::new(1),
431 last_log_index: 0,
432 last_log_term: RaftTerm::default(),
433 };
434 let req2 = VoteRequest {
435 term: RaftTerm::new(1),
436 candidate_id: NodeId::new(3),
437 last_log_index: 0,
438 last_log_term: RaftTerm::default(),
439 };
440 node.process_vote_request(&req1);
441 let resp2 = node.process_vote_request(&req2);
442 assert!(!resp2.vote_granted);
443 }
444
445 #[test]
446 fn test_vote_deny_stale_log() {
447 let mut node = make_node(2);
448 node.log.append(LogEntry::new(RaftTerm::new(2), 1, "x"));
450 let req = VoteRequest {
451 term: RaftTerm::new(3),
452 candidate_id: NodeId::new(1),
453 last_log_index: 0,
454 last_log_term: RaftTerm::new(1), };
456 let resp = node.process_vote_request(&req);
457 assert!(!resp.vote_granted);
458 }
459
460 #[test]
461 fn test_election_timer_expired() {
462 let timer = ElectionTimer::new(150, 1000);
463 assert!(!timer.is_expired(1100));
464 assert!(timer.is_expired(1150));
465 assert!(timer.is_expired(1200));
466 }
467
468 #[test]
469 fn test_election_timer_reset() {
470 let mut timer = ElectionTimer::new(150, 1000);
471 timer.reset(1100);
472 assert!(!timer.is_expired(1200)); assert!(timer.is_expired(1250));
474 }
475
476 #[test]
477 fn test_node_id_display() {
478 let id = NodeId::new(42);
479 assert_eq!(format!("{}", id), "Node(42)");
480 }
481
482 #[test]
483 fn test_raft_term_ordering() {
484 assert!(RaftTerm::new(5) > RaftTerm::new(3));
485 assert!(RaftTerm::new(1) < RaftTerm::new(2));
486 assert_eq!(RaftTerm::new(4), RaftTerm::new(4));
487 }
488}