BOOKS DEEP-READ · SRE · CH 23
Site Reliability Engineering · Ch 23 · Google (Laura Nolan et al.) · 2016
Name one person on call and the work gets done. Let two people each believe they're in charge and everyone starts overwriting everyone else. A pile of machines faces that question all day long: which one is the primary? Who holds the lock right now? SRE Chapter 23 is about exactly this — how a group of machines reliably agrees on one story.
In October 2018, GitHub lost connectivity between its East Coast network hub and its primary East Coast data center for 43 seconds. Forty-three seconds — and the site was degraded for a full day. During those 43 seconds the East and West Coast databases each accepted writes; once the link came back, both sides held data the other did not, and neither could simply overwrite the other. Forty-three seconds of not-talking bought 24 hours of manual repair.
The folk remedy: the primary shouts "still alive" every second, and the standby promotes itself after a few silent seconds. It sounds reasonable, but it has a dead end — silence doesn't tell you whether the other side died or the phone line was cut. If it was only the line, the other side is perfectly healthy, and the moment you promote yourself there are two people in charge. That's split-brain. Worse, the folk remedy looks completely fine on ordinary days and only shows its teeth on the day you least want it to.
Say you have five machines and every decision needs three votes. Here's the trick: two groups can never each collect three votes at the same time. Three plus three is six, which is more than five, so some machine would have to be counted twice — and it will not cast the same vote for two contradictory things. "Two people in charge" is ruled out at the root. When the network splits, the smaller side simply stops working — a pause you can recover from, unlike diverged data.
The second trick is keeping one shared ledger: machines don't each keep their own books. Every change is copied into the same ledger in exactly the same order. Same entries, same order — replay them and every machine necessarily lands on the same answer.
The good news: this has been written and beaten on for twenty years — Google's Chubby, and the open-source ZooKeeper and etcd (the Kubernetes you use keeps its entire cluster state in etcd). The most practical line in this chapter is: don't build your own. You think you're writing "a simple primary failover"; you're writing a consensus algorithm, and you're probably writing it wrong.
The honest cost: every decision waits for a majority to nod, so it is inherently slower than one machine, and slower still the farther apart the machines sit — a round trip across a continent costs a hundred-odd milliseconds at the speed of light.
When a group of machines must agree on "who's in charge and what the state is," the only reliable recipe is a majority vote plus one shared ledger; heartbeats-and-timeouts will split-brain sooner or later. And this is a thing you take off the shelf, not write yourself.
Want the mechanisms, the timing diagrams and the selection tables? → Switch to the deep read
The chapter makes a single claim: the moment a group of processes must agree on some state, you are facing distributed consensus — there is no shortcut. Heartbeats, timeouts and timestamps are not ways around consensus; they are an incorrect implementation of it, and the incorrectness only shows up on the day the network partitions.
This chapter lives in Part IV of the SRE book, "Managing Complex Systems," and is written by Laura Nolan. The two preceding chapters (Ch 21 overload, Ch 22 cascading failures) are about how a single service avoids destroying itself; this one steps up a level to how several machines keep their critical state correct. It is the most theoretical chapter in the book and maps directly onto DDIA Chapter 9; Ch 25 and Ch 26 both assume the state underneath them is trustworthy.
The chapter opens with three real (anonymized) outages that share one trait — each tried to avoid consensus. ① Split-brain: a primary/standby pair used heartbeats for liveness; network congestion timed the heartbeat out, the standby promoted itself, and the primary had never died. Both wrote files, reconciliation was manual, and some data was lost for good. ② Failover requires a human: another system avoided split-brain by declaring that only a person may designate the primary — so availability was pinned to human reaction time, plus a great deal of toil. ③ A homegrown group-membership algorithm: gossip for membership plus custom liveness checks; under network trouble the cluster split into two groups each convinced the other was dead, and wrote to both.
The author's diagnosis is unsparing: these systems needed consensus all along; their authors just wouldn't admit it. "Heartbeat plus timeout" treats a fundamentally unsolvable question — in an asynchronous network, does silence mean dead or merely slow? — as if it were solvable. The price isn't "one extra failover now and then." It is diverged data, and once data has diverged, nothing automatic can decide for you which copy is right.
While we're here, a widely mangled point: CAP says that during a partition you choose between consistency and availability — not that you must pick two of three on an ordinary Tuesday. Consensus systems choose C: the minority side would rather refuse service than diverge. That trades an irreversible outcome (split-brain) for a recoverable one (a short outage).
The root cause: in an asynchronous network, "crashed" and "slow" are indistinguishable. A missing heartbeat may be a dead process, or a long GC pause, or congestion on one hop — no external observation separates the two. All you can pick is a threshold for "how long counts as dead," and whatever you pick, some day it declares a healthy peer dead. This is what the 1985 FLP impossibility result says: in a fully asynchronous model, if even one process may crash, no deterministic algorithm both guarantees safety and guarantees a decision in bounded time.
Real systems make the trade in one direction only: give up "always terminates," never give up "always safe" — using timeouts and randomization (that's what Raft's randomized election timeout is for) to break ties. The cost is that on a truly bad network you may re-elect repeatedly and stay leaderless for a while, but you never end up with two leaders. The folk remedy makes the trade backwards: promote on timeout, and split-brain follows.
Pillar one: quorum intersection. Any two majorities share at least one member. Among five replicas, any two groups of three must overlap — and the overlapping replica remembers it already voted in the previous round, so it refuses to vote for a conflicting decision. Two disjoint majorities cannot exist, and split-brain is ruled out at the root. It also explains the preference for odd counts: six replicas still need four for a majority, tolerate the same two failures as five, and cost you one extra machine and one extra unit of latency.
Pillar two: the replicated state machine. What consensus really produces is not "one agreed value" but a log whose contents and order are identical on every replica. Each replica applies the same deterministic operations in the same order, so the states cannot drift. As the chapter puts it plainly: consensus is the building block; the replicated state machine is the thing you actually wanted — databases, lock services, config stores and message queues are all the same RSM behind a different interface.
Paxos (Leslie Lamport) has a two-round skeleton. ① Claim a proposal number — a candidate carries a globally increasing number to a majority of replicas and asks "may I propose?"; each replica that agrees promises never to accept a smaller number afterwards and reports back the latest value it has already accepted. ② Propose and commit — the candidate proposes a value, but if round one surfaced any already-accepted value it must reuse that value; a majority accepting seals it. That "must reuse" rule is the entire secret of never contradicting a past decision: the new majority necessarily overlaps the old one, so it always sees the old decision and cannot overturn it.
Multi-Paxos is the practical form: running two full rounds per entry is expensive, so a stable leader takes round one once, and every later entry needs a single round — putting one write at one network round trip plus one fsync. Raft (2014) reorganizes the same thing into leader election, log replication and safety, with understandability as an explicit design goal. It is no faster and no safer than Paxos, but engineers can read it and implement it correctly — which is itself reliability. etcd, Consul, TiKV and Kafka's KRaft all use Raft; Zab is ZooKeeper's variant.
One order-of-magnitude difference that gets overlooked: all of these tolerate only crash failures — nodes die but never lie — and tolerating f failures needs 2f + 1 replicas. Tolerating Byzantine failures, where a node sends contradictory messages on purpose, jumps to 3f + 1. Inside a data center, almost everyone builds only the former.
IdRegistry so that ad clicks joined across data centers are never billed twice.The lethal detail: a distributed lock must be a lease, and it must carry an increasing number. A lock holder can be frozen by a long GC pause, a VM suspend or a network problem without ever noticing; by the time it wakes, the lease has expired and someone else holds the lock — while it still believes it does. The fix is for the lock service to hand out a monotonically increasing sequencer (a fencing token), and for the downstream store to check it and reject writes carrying a stale one. Holding a lock is not correctness — the single most quotable line in the chapter.
The chapter goes out of its way to rebut a widespread line: consensus algorithms are too slow and too low-throughput for serious systems. They are indeed more expensive than a single machine, but the expense sits in two predictable places. Network round trips — a commit waits for a majority to answer, so the floor on write latency is the round trip to the third-fastest replica (with five). Intra-data-center RTT is typically sub-millisecond, cross-DC within a region a few milliseconds, cross-continent tens of milliseconds, and trans-oceanic a hundred-plus milliseconds — that floor is set by the speed of light. Durable writes — the log must be fsynced before you dare acknowledge: typically a few milliseconds on spinning disks, down to sub-millisecond on SSD/NVMe.
The optimizations are a short list. A stable leader removes phase one. Batching merges many proposals into one fsync and one broadcast, lifting throughput at a small cost in per-entry latency. Pipelining sends the next entry before the previous is acknowledged while preserving order, and pays off most on high-bandwidth, high-latency links. Fast Paxos lets clients send directly to all replicas, skipping the hop through the leader, at the price of a larger quorum. Quorum leases give some replicas a read lease over some of the data, so reads are local and still strongly consistent. And since the leader itself is the throughput ceiling, shard into many consensus groups — Spanner runs one Paxos state machine per tablet.
How many. n replicas tolerate (n-1)/2 failures. Google's default is five, for a very practical reason: you want to survive one unexpected failure while one replica is down for planned maintenance. Three replicas can't do that — during the maintenance window you have zero redundancy. Beyond five, fault tolerance grows slowly while latency and cost keep climbing.
Where. The iron rule is that a majority must not sit inside one failure domain. Five replicas across three data centers (2 + 2 + 1) survive the loss of any one; all five in one room means the room takes everything with it. But the wider you spread, the higher the write latency, because a majority now includes a distant replica. The leader should sit near where the writes are. That's the other half of the GitHub lesson: allowing automated promotion across a continent lets the system make a decision that is bad for both latency and consistency at the worst possible moment.
What to watch. Healthy replica count; whether a leader exists and how often it changes; log index and how far each replica lags; proposals seen versus accepted; throughput and latency distribution; network round-trip time and fsync wait. A rule of thumb: a flapping leader, or one persistently lagging replica, predicts trouble earlier than "overall latency is a bit high" — it means the system is already re-electing repeatedly and simply hasn't fallen over yet.
Table 1 · Homegrown heartbeat+timeout vs. an off-the-shelf consensus system
| Homegrown heartbeat + timeout | Off-the-shelf (Chubby / ZooKeeper / etcd) | |
|---|---|---|
| On a normal day | Just as good, maybe faster (one less hop) | One extra round trip, slightly slower |
| Under partition | May produce two primaries → divergence, no automatic recovery | Minority side refuses service, never diverges |
| Failover | Either wrong promotions, or page a human | Automatic, typically within seconds |
| Membership change | The part homegrown code gets wrong most often | Built into the algorithm, proven at scale |
| Operational cost | Looks free; is hidden debt | One more stateful cluster to run |
Table 2 · Replica count and placement: tolerance, latency, cost
| Configuration | Tolerates | Write latency | Verdict |
|---|---|---|---|
| 3 replicas | 1 failure | — | Fine at small scale, but zero redundancy during maintenance |
| 5 replicas | 2 failures | — | Google's default; the chapter's recommendation |
| 4 / 6 (even) | Same as the odd number below | — | Pure waste: 4 replicas still tolerate only 1 |
| All in one data center | Machine / rack failure | sub-ms RTT + fsync | Losing the site loses everything |
| 3 sites in one region | Any single site | a few ms + fsync | Usually the sweet spot |
| Cross-continent / ocean | Regional disaster | tens to 100+ ms | Latency floor set by physics; redesign the interaction model |
Table 3 · What you want to do → which shape to use
| Need | Right shape | Common mistake |
|---|---|---|
| Elect exactly one primary | Leader election inside a consensus system | Homegrown heartbeat + virtual IP → split-brain |
| Store config / service discovery | Replicated config store (low volume, high value) | Dumping bulk business data into ZooKeeper |
| Exclusive access to a resource | A lease with an increasing fencing token | Assuming you still hold the lock; no downstream check |
| Run a task exactly once | A queue on top of consensus, or a unique-ID registry plus idempotence | "It probably won't run twice" |
| Strongly consistent state across sites | A consensus group spread over failure domains | Resolving conflicts by comparing timestamps |
This chapter answers an interview question you will almost certainly be asked: "how would you build highly available primary failover?" The expected answer is not "heartbeats and a virtual IP" — it is hand leader election to a consensus system, and fence writes downstream with a token. Nearly everything around you is an instance of it: Kubernetes keeps all cluster state in etcd (Raft); ZooKeeper (Zab) long served as the coordination layer for HBase and older Kafka; CockroachDB and TiKV made "one Raft group per data shard" standard; Spanner runs one Paxos group per tablet.
100–1000 tablets — confirming that sharding into many consensus groups is the way around the leader's throughput ceiling. Corbett et al., "Spanner", OSDI 2012 ↗IdRegistry; the paper reports a production deployment that "processes millions of events per minute at peak with an average end-to-end latency of less than 10 seconds" and tolerates data-center-level outages without manual intervention — a direct rebuttal of "consensus is too slow for real systems." Ananthanarayanan et al., "Photon", SIGMOD 2013 ↗① If a group of processes must agree on state, you are doing distributed consensus; heartbeat-plus-timeout doesn't avoid it, it implements it incorrectly.
② Root cause: in an asynchronous network "crashed" and "slow" are indistinguishable; FLP says you cannot have both safety and guaranteed termination, and real systems give up the latter and keep the former.
③ Two pillars: majority quorums (any two majorities overlap, so split-brain is ruled out) and the replicated state machine (one log, one order, deterministic execution).
④ Algorithms: Paxos's two rounds, with "must reuse an accepted value" as the secret of never contradicting itself; Multi-Paxos/Raft use a stable leader to reach one RTT + one fsync per write. Crash tolerance needs 2f+1 replicas, Byzantine tolerance 3f+1.
⑤ Five shapes: RSM / config store / leader election / coordination and locking / reliable queues. Locks must be leases with increasing tokens, checked downstream — a lock alone is not correctness.
⑥ Performance: the cost is network RTT (sub-ms in a data center, 100+ ms trans-oceanic) and fsync; batching, pipelining and sharding into many consensus groups are the standard answers.
⑦ Deployment: five replicas is the recommendation; a majority must never share a failure domain; put the leader near the writes. Watch leader change rate and replica lag first.
⑧ The most practical line: don't build your own consensus. Use Chubby / ZooKeeper / etcd / Consul, and spend your effort on replica count, placement and fencing.