1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
///|
/// A single Raft node: its identity, current role, and the state it holds.
///
/// `current_term`, `voted_for` and `log` make up the state a real deployment
/// must persist to stable storage before replying to any RPC, so the node
/// recovers correctly after a crash. `commit_index` and `last_applied` are
/// volatile: they are safe to lose and are rebuilt after a restart.
pub struct Node {
  id : String
  mut role : Role
  mut current_term : UInt64
  mut voted_for : String?
  log : Array[Entry]
  mut commit_index : UInt64
  mut last_applied : UInt64
  mut snapshot_index : UInt64
  mut snapshot_term : UInt64
}

///|
/// Create a fresh node that starts as a follower at term 0 with an empty log.
pub fn Node::new(id : String) -> Node {
  {
    id,
    role: Follower,
    current_term: 0,
    voted_for: None,
    log: [],
    commit_index: 0,
    last_applied: 0,
    snapshot_index: 0,
    snapshot_term: 0,
  }
}

///|
/// The node's current role.
pub fn Node::role(self : Node) -> Role {
  self.role
}

///|
/// The node's current term.
pub fn Node::current_term(self : Node) -> UInt64 {
  self.current_term
}

///|
/// Step down to follower and adopt a newly observed, higher term, clearing
/// any vote cast in the old term.
pub fn Node::become_follower(self : Node, term : UInt64) -> Unit {
  self.role = Follower
  self.current_term = term
  self.voted_for = None
}

///|
/// Enter the pre-vote probe phase (etcd's `becomePreCandidate`): change the role
/// but NOT the term or vote — a pre-candidate solicits votes under a hypothetical
/// next term it has not adopted.
pub fn Node::become_pre_candidate(self : Node) -> Unit {
  self.role = PreCandidate
}

///|
/// Start a new election: advance the term and vote for self.
pub fn Node::become_candidate(self : Node) -> Unit {
  self.current_term = self.current_term + 1
  self.role = Candidate
  self.voted_for = Some(self.id)
}

///|
/// Take leadership after winning a majority of votes in the current term.
pub fn Node::become_leader(self : Node) -> Unit {
  self.role = Leader
}

///|
/// Advance the commit index once a higher index is known to be committed.
/// Never moves backwards.
pub fn Node::advance_commit(self : Node, index : UInt64) -> Unit {
  if index > self.commit_index {
    self.commit_index = index
  }
}

///|
/// Record that the state machine has applied entries up through `index`.
pub fn Node::mark_applied(self : Node, index : UInt64) -> Unit {
  self.last_applied = index
}