Library
14 min read

Consensus with Raft

A Kubernetes control plane stores everything it knows (every pod, every deployment, every secret) in etcd, usually on three machines. Lose one and nothing happens. Lose two and kubectl apply hangs, even though the last etcd node is healthy and holds a complete copy of the data.

A healthy machine with all the data, refusing to accept a write. That is not a bug. It is the rule that stops a cluster from ever disagreeing with itself, and this chapter is about why the rule is right.

Your answer

Five servers must agree on the order of writes. Any two can fail, and the network can split. How many servers must have a write before you tell the client it is saved, and why exactly that number?

What consensus is for#

Replication with one primary and some replicas works well until the primary dies. Then someone has to pick a new primary, and the dangerous moment is when the old one comes back still believing it is in charge: two primaries, two histories, split brain. Doing that failover safely, automatically, is the job of consensus.

The shape is always the same. A group of machines (almost always 3 or 5) keeps a replicated log: an ordered list of commands like "set x = 2". Every machine applies the same commands in the same order, so every machine ends up in the same state. Consensus guarantees that once an entry is committed at a position in the log, no machine will ever have a different entry at that position, whatever crashes or splits happen.

You rarely write this yourself. You use a system that does it, and put your critical, small, must-agree data in it: which node leads, which shard lives where, the cluster's configuration.

Built on a consensus log
  • etcdRaft. Holds all Kubernetes state
  • ConsulRaft. Service catalogue and locks
  • Apache KafkaKRaft: Raft for cluster metadata since ZooKeeper was removed in 4.0
  • CockroachDBOne Raft group per range of keys, thousands per cluster
  • MongoDBReplica set elections use a Raft-style protocol
  • ZoZooKeeperZab, a close cousin. Used by older Kafka, HBase and Hadoop

Why a majority#

A write counts as committed once a majority of the group has it: 2 of 3, 3 of 5. The reason is one fact about majorities: any two majorities of the same group share at least one machine. Whatever happens next, any future majority, for an election or for another write, includes at least one machine that saw this write. It cannot be forgotten.

That also tells you how many failures a group survives:

MachinesMajorityCan lose
321
431
532
743

Four machines tolerate exactly as many failures as three, and write more slowly, because every write now waits for three acknowledgements instead of two. So consensus groups are odd sized. And now the cold open makes sense: one etcd node out of three is not a majority. If it accepted writes on its own, the other two might be alive on the far side of a network split, accepting different writes. Refusing is the only safe choice.

Terms: a counter for leadership#

Raft has a single leader that takes all writes, and followers that copy it. Time is divided into terms, numbered 1, 2, 3 and up. Each term starts with an election and has at most one leader.

The term number works like the Lamport clock from the clocks chapter, for leadership. Every message carries the sender's term. Any node that sees a higher term than its own immediately adopts it, and if it was a leader, it steps down on the spot. A leader that was cut off and missed an election finds out the moment it hears from anyone in the newer term.

Electing a leader#

The leader sends a heartbeat to every follower many times a second. A follower that hears nothing for its election timeout assumes the leader is gone and stands for election itself. The original paper suggests a timeout chosen at random between 150 and 300 ms, and the randomness matters: it makes one follower almost always time out first and win before anyone else starts.

1/6 The leader dies. Heartbeats stop.

The second rule on the vote is where safety comes from. A node only votes for a candidate whose log is at least as up to date as its own: a later last term, or the same last term and at least as long. Every committed entry is on a majority, the winner needs votes from a majority, and those two majorities overlap. So the winner always holds every committed entry, because any voter that had one more would have refused.

Replicating the log#

Once there is a leader, a write works like this:

1/6 Clients only talk to the leader. A follower that gets a request redirects it.

One round trip to the nearest majority, plus a disk write on each. That is the cost of every committed write, and it is the number to remember when someone proposes putting consensus on a hot path.

Break it#

Here is a five node group you can damage. Start by splitting the network so the leader ends up on the small side, then write through both sides, then reconnect. After that, stop two nodes, then a third, and try writing.

Five nodes, one log
committedin the log, not committed
Network
Stop or restart a node
A is the leader for term 1, and all five nodes hold x=1, committed. Try this: split the network, write through A, write through C, then reconnect.
A model, simplified: real elections use randomised timers, and a new leader only counts older entries as committed once it commits one of its own. The rules that keep data safe are all here.

Three things to take from it.

The old leader never stops believing. Cut off with only one follower, A still thinks it leads and still accepts writes. It just cannot commit any of them, because it cannot reach three nodes. The clients writing to it wait and time out.

The majority side moves on. C, D and E elect a new leader in a higher term and keep serving writes. Two leaders exist at once, but only one of them can commit, so there is no split brain in the data.

Healing throws away only what was never promised. When the network comes back, A sees term 2, steps down, and its uncommitted entries are overwritten. No client was ever told those writes succeeded, so nothing anyone relied on is lost. Those clients still need to retry, which is why every write to a consensus system should carry an idempotency key.

It cannot tell you whether your write happened

When a request to the old leader times out, the client has the problem from Partial failure: the write may have been discarded, or it may have been committed just before the leader was cut off. Consensus keeps the replicas consistent with each other. It does not tell the client what happened. Retry with the same key.

Reading from a Raft group#

Writes are safe. Reads have a subtle problem: the leader you ask might have been replaced a moment ago without knowing yet, exactly like A in the widget. If it answers from its own state, it might return a value that a newer leader has already overwritten.

Three ways to read, in order of cost:

Read from any follower. Fast and spreads the load, and possibly stale. etcd calls this a serializable read (--consistency=s).

Read from the leader, after it confirms it is still the leader. The leader notes its commit point, exchanges one heartbeat round with a majority, and then answers. This is called ReadIndex. It is etcd's default, which it calls a linearizable read: one extra round trip, no disk write.

Read from the leader under a lease. The leader assumes nobody else can be elected for a little while after a successful heartbeat, and answers without a round trip. This is fast, and it quietly depends on clocks running at roughly the same rate, which the clocks chapter gave you reasons to doubt.

Raft and Paxos#

Paxos is the older answer to the same problem. Leslie Lamport described it around 1989 and published it in 1998, and its reputation is that almost nobody understands it on the first read. The basic algorithm agrees on one value. Building a full replicated log out of it (Multi-Paxos) was left largely to implementers, who each filled the gaps differently.

Raft was published by Diego Ongaro and John Ousterhout in 2014, with understandability as the explicit goal. It has a strong leader, says exactly how elections, log repair and membership changes work, and gives the same guarantees: a group of 2f + 1 machines survives f failures and never commits two different entries at the same position.

In practice they are equivalent for an interview. Google's Chubby and Spanner use Paxos; etcd, Consul, CockroachDB, TiKV and Kafka's KRaft use Raft. If asked "how do the replicas agree?", "a consensus protocol such as Raft or Multi-Paxos, with a majority quorum" is the complete answer, and the mechanism above is the follow up.

What consensus costs#

Latency. Every commit waits for a majority round trip and a disk flush on each. Inside one region that is a few milliseconds. Put the replicas in Mumbai, Singapore and Frankfurt and every write pays the round trip to the nearer of the other two.

Throughput does not grow with machines. All writes go through one leader, and more followers only add work for it. A bigger group is more fault tolerant and slower. Systems that need throughput run many small groups side by side, one per shard, which is what CockroachDB and TiKV do.

Size. etcd keeps its whole data set in memory-mapped files and limits it to 2 GB by default. Consensus stores are for metadata measured in megabytes: who leads, what lives where, configuration and locks. Your orders table belongs somewhere else.

ChoiceWhat you gainWhat you payPick it when
Three nodesFastest commits: the leader needs only one other acknowledgement.Survives one failure. During maintenance on one node, one more failure stops writes.Most clusters, such as a Kubernetes control plane in one region.
Five nodesSurvives two failures, so you can patch one node while still tolerating another.Each commit waits for two other acknowledgements, and there are more machines to run.Critical coordination services, and groups spread across three availability zones.
Consensus instead of async primary and replicaAutomatic failover that never loses a committed write or allows split brain.Every write waits for a majority, and a minority side cannot write at all.Metadata, configuration, locks, and databases that promise no lost writes.
One group across regionsSurvives losing a whole region.Every write pays a cross-region round trip, tens to hundreds of milliseconds.Data that must survive a regional outage, with writes rare enough to afford it.

Interview replay#

Interviewer
Your job scheduler needs exactly one active scheduler. How do the instances agree on which one?
An invitation to say "consensus" and then prove you know what it costs.
You
I would not build agreement myself. I would run the schedulers against etcd, three nodes, and have them campaign for a leader key with a lease. etcd uses Raft, so only one candidate can win, and the lease expires if the winner dies.
Reaches for a proven store rather than inventing a protocol. That is the senior move.
Interviewer
Why three etcd nodes and not two?
You
Two would need both to agree, so one failure stops it. Three survive one failure with a majority of two. Four is no better than three, so it is three or five.
The majority table, out loud, with the even-number point.
Interviewer
The old scheduler gets cut off from etcd but keeps running. What stops it from scheduling jobs too?
The real question. Consensus protects etcd's data, not the side effects of the processes using it.
You
Nothing in Raft does. It will lose its lease, but it might not notice in time, for example during a GC pause. So each scheduling write carries a fencing token from etcd, the lease revision, and the job store rejects anything older than the newest token it has seen.
Separates agreement on who leads from making the old leader harmless. Few candidates get here.
Interviewer
How long does failover take?
You
The lease TTL plus an election. With a 10 second lease, around 10 to 15 seconds without a scheduler. I have not measured etcd elections under load myself, so I would test it before promising a number.
A number with its parts, and honest about what is reasoning rather than experience.

Checkpoint#

Checkpoint

1. How many node failures does a five node Raft group survive while still accepting writes?

2. Why can a newly elected Raft leader never be missing a committed entry?

3. A leader is cut off with one follower out of five. Clients keep sending it writes. What happens to them?

Worth memorising
  • 2f + 1 nodes survive f failures. 3 survive 1, 5 survive 2. Even sizes add nothing.
  • Committed = on a majority. Any two majorities overlap, so it cannot be forgotten.
  • Higher term wins, always. A stale leader steps down the moment it sees one.
  • Every write costs a majority round trip plus a disk flush, and one leader takes them all.
Say this in 60 seconds

Consensus lets a small group of machines, usually three or five, agree on one ordered log even when some crash or the network splits. Raft does it with a single leader per numbered term. Followers that stop hearing heartbeats wait a random timeout, stand for election in a higher term, and win with a majority of votes, and voters only back a candidate whose log is at least as complete as theirs. The leader appends each write and commits it once a majority has it on disk. Because any two majorities overlap, a committed entry survives every failure, and 2f plus 1 nodes tolerate f failures. A leader cut off in a minority keeps accepting writes but cannot commit them, and they are overwritten when it rejoins, so clients must retry with an idempotency key. It costs a majority round trip per write, so I use it for metadata, leadership and locks, through etcd or similar, not for bulk data.