Raft elects a leader by having nodes request votes from their peers; a candidate that gets a majority becomes leader. This exercise models a SINGLE node's perspective on that process, driven by explicit method calls rather than real timers or network RPCs -- deterministic and fully testable.
A node is always FOLLOWER, CANDIDATE, or LEADER. start_election() and
receive_append_entries() are done for you. Implement:
receive_vote(voter_id): record a vote received whileCANDIDATE(do nothing if we're not currently a candidate). Ifvotes_receivednow exceeds a majority ofcluster_size, becomeLEADER.grant_vote(candidate_id, candidate_term): Raft's vote-granting safety rule. Refuse (return False, no state change) ifcandidate_term < current_term(stale candidate). Refuse ifcandidate_term == current_termbut we already voted for someone ELSE this term (one vote per term). Otherwise: adoptcandidate_term, record the vote, become/stayFOLLOWER, andreturn True.
Example
node = RaftNode("n1", cluster_size=5) # majority = 3
node.start_election()
node.receive_vote("n2") # 2 of 5 -- still CANDIDATE
node.receive_vote("n3") # 3 of 5 -- now LEADER
Constraints
- "Majority" means strictly more than half of
cluster_size-- forcluster_size=5that's> 2.5, i.e. 3 votes. - A vote can only be granted ONCE per term -- a second candidate asking in the same term (that we haven't already voted for) must be refused.
Real-world grounding
- etcd's Raft implementation (the actual consensus algorithm; note it lives
in a separate repo from etcd itself):
etcd-io/raft
Real Raft election is driven by RANDOMIZED election timeouts specifically to avoid two nodes becoming candidates simultaneously and splitting the vote forever -- this exercise skips the timer/randomization layer (which is exactly the "time" complexity ruled out by this course's determinism constraint) and focuses on the state transitions and safety rules underneath it, which are identical either way.