// lesson: election-timing

Election Timing in a Deterministic World

Raft's liveness hinges on timing (ยง5.6, "Timing and availability"). Followers expect a heartbeat from the leader well before their election timeout expires; the paper's rule of thumb is

// broadcastTime << electionTimeout << MTBF

If every server used the same election timeout, they would all time out together, all become candidates together, split the vote, and repeat forever. Raft's fix is charmingly low-tech: randomized election timeouts (e.g. 150โ€“300 ms). Whoever draws the shortest timeout usually wins the election before anyone else even wakes up. The paper's own experiments (ยง9.3, "Performance") back this recommendation: shrinking the timeout range brings faster failover, but push it too low (well under the broadcast time) and leaders start missing their own heartbeat deadline before followers' timeouts fire, causing unnecessary elections and lower availability โ€” which is exactly the broadcastTime << electionTimeout half of the inequality above. 150โ€“300 ms is the paper's own conservative recommendation, chosen to avoid that failure mode while still detecting a crashed leader quickly.

Time as data

Randomness and wall clocks are poison for tests. Production-grade Raft implementations (etcd's raft library is the famous example) therefore model time as an integer: the node has no timer at all, just a Tick() method the host calls at a fixed cadence, plus counters:

  • ElectionElapsed โ€” ticks since the last heartbeat/vote-grant; when it reaches ElectionTimeout, the election fires.
  • HeartbeatElapsed โ€” ticks since the leader last broadcast; when it reaches HeartbeatTimeout, the leader sends heartbeats.

The randomized timeout becomes plain data: something outside the node picks ElectionTimeout (randomly in production, explicitly in tests) and stores it in the struct. A node with ElectionTimeout = 4 deterministically loses patience before one with ElectionTimeout = 6 โ€” which is exactly how we will stage elections on purpose in the final challenge.

Nodes emit, they do not send

The second determinism trick: a node never talks to a network. Stepping a node returns the messages it wants sent and lets the caller deliver them. The node stays a pure state machine; the messy parts (sockets, retries, delays, partitions) live outside and can be simulated. In this challenge Tick returns a []Message; peers are listed in Peers, and to keep everything reproducible you must emit messages in Peers order.

What can a tick emit?

  • A follower or candidate whose ElectionElapsed reaches ElectionTimeout starts an election exactly as in lesson 1 โ€” and emits a MsgRequestVote to every peer, carrying its LastLogIndex/LastLogTerm for the ยง5.4.1 check.
  • A leader whose HeartbeatElapsed reaches HeartbeatTimeout emits a MsgHeartbeat (an empty AppendEntries) to every peer, carrying its LeaderCommit so followers can advance their commit index.
  • Everything else: no messages, just a counter bumped.

Leaders never run an election timer โ€” receiving valid heartbeats is the follower's job, sending them is the leader's.

โ€บ Drive a Node with Ticks

35 pts

Implement Tick(n *Node) []Message, one clock tick for one node.

Rules:

  1. Leader: increment HeartbeatElapsed. If it is still below HeartbeatTimeout, return no messages. Otherwise reset it to 0 and return one MsgHeartbeat per peer, in Peers order, with Term = CurrentTerm and LeaderCommit = CommitIndex. Leaders never touch ElectionElapsed.
  2. Follower or candidate: increment ElectionElapsed. If it is still below ElectionTimeout, return no messages. Otherwise the election fires: reset ElectionElapsed to 0, become Candidate, increment CurrentTerm, set VotedFor = n.ID and VotesGranted = 1. If that is already a majority of the cluster (len(Peers)+1 servers), become Leader immediately and return no messages. Otherwise return one MsgRequestVote per peer, in Peers order, with Term = CurrentTerm and the node's last log index and term (1-based; empty log โ†’ 0 and 0).

Set From and To on every message. Fields that do not apply to a message type stay zero.

Log in to submit a solution and earn points.