Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

System Design Fundamentals  /  Consensus & Coordination
Fundamental 04~14 min readAdvanced
Deep Dive

Consensus: nodes crash,
the cluster still agrees.

Machines die, messages vanish, clocks drift — and yet a distributed system must still settle on exactly one answer: one leader, one next log entry, one commit decision. That's the consensus problem, the hardest in the field, and the algorithms that solve it underpin almost everything reliable.

01

Why agreement is hard

Consensus means getting a group of nodes to agree on a single value, even when some fail. It sounds trivial until you add reality: messages arrive late or never, nodes crash and recover, and you can't tell a slow node from a dead one. The FLP impossibility result (Fischer, Lynch and Paterson, 1985) proves that in a fully asynchronous network, no deterministic algorithm can guarantee it will ever reach a decision if even one node may crash. So real systems split the promise: they are always safe, never deciding two different values, and they use timeouts and randomization to make progress whenever the network behaves well enough.

Many everyday problems are consensus in disguise: electing a leader, agreeing the next entry in a replicated log, deciding whether a distributed transaction committed, or maintaining a distributed lock. Solve consensus once and you get all of them.

02

Quorums: agreement by majority

The core trick is the majority quorum. If every decision needs votes from a strict majority, then any two decisions must share at least one node — because two majorities of the same set always overlap. That single overlapping node can't vote for two conflicting values, so two leaders or two conflicting commits can never both succeed, even during a partition.

3 of 5 is a majority — and any two majorities overlap
✓
✓
✓
·
·
A 5-node cluster tolerates 2 failures: 3 survivors still form a majority and keep deciding. This is why clusters are sized at odd numbers.

It's all counting. A majority is ⌊N/2⌋+1, a cluster survives N−majority failures, and because two majorities sum to more than N they must share a node — so at most one leader per term:

majority(5)=3, tolerates 2  ·  majority(4)=3, tolerates 1  →  4 nodes = same tolerance as 3: size odd

And 3+3 > 5, so two candidates can't both reach a majority — Raft's single-leader guarantee, from pure arithmetic. The runnable version below runs an election and the fault-tolerance table.

These majority quorums are not the tunable W and R quorums of leaderless stores. A W + R > N quorum guarantees that two sets of replicas overlap, but it never gets nodes to agree on an order of operations, which is why it can't give you a safe compare-and-set. The CAP handbook works through where those quorums break.

03

Raft & Paxos

The two famous consensus algorithms. Same guarantee, different ergonomics.

Raft was designed to be understandable. It elects a single leader for a term; the leader takes all writes, appends them to a replicated log, and an entry is committed once a majority has stored it. If the leader goes silent, followers time out and a new election starts. It powers etcd, Consul, and CockroachDB.

Raft — elect, then replicate
Follower
times out
→
Candidate
requests votes
→
Majority?
→ Leader
→
Replicate log
to followers
A higher term always wins, so a recovered old leader steps down — no split brain.

Paxos reaches the same safety with proposers, acceptors, and learners, but is famously hard to reason about and implement; Multi-Paxos adds a stable leader for efficiency. The first rule is to reach for an existing implementation rather than writing your own. The internals below still matter: they are what an interviewer probes with "what happens if the leader crashes mid-write?", and they explain how the systems you run fail.

04

Inside Raft: terms, votes and the log

Raft is safe because of a few rules, and each one closes a specific hole.

Terms are Raft's logical clock. Each term starts with an election and has at most one leader, and every message carries the sender's term. A node that sees a higher term adopts it and becomes a follower; a message with a lower term is rejected. That one rule is how a deposed leader finds out.

Randomized election timeouts stop elections colliding. A follower that hears nothing from a leader for a random interval (the Raft paper suggests 150–300 ms) starts an election. Usually one node times out first, wins, and sends heartbeats before anyone else wakes; if two candidates split the vote, both time out again with fresh random waits. The timeout must sit well above the network round trip and well below the time between failures.

The vote rule keeps committed entries alive across leader changes. A node votes at most once per term, and only for a candidate whose log is at least as up to date as its own: the later last-entry term wins, and on a tie the longer log wins. A committed entry sits on a majority and a winner needs votes from a majority, so some voter holds every committed entry and refuses any candidate missing one. Every new leader therefore already has every committed entry.

# RequestVote: at most one vote per term, only for an up-to-date log
def grant_vote(me, term, cand, cand_last_term, cand_last_index):
    if term < me.term:
        return False                          # stale candidate
    if term > me.term:
        me.term, me.voted_for = term, None    # new term: become follower, forget old vote
    up_to_date = (cand_last_term, cand_last_index) >= (me.last_term(), me.last_index())
    if me.voted_for in (None, cand) and up_to_date:
        me.voted_for = cand                   # persist to disk before replying
        return True
    return False

# AppendEntries: accept only if our log matches the leader's just before the new entries
def log_matches(me, prev_index, prev_term):
    return prev_index == 0 or me.term_at(prev_index) == prev_term

Log matching is how the leader repairs followers. Each AppendEntries message carries the index and term of the entry just before the new ones, and a follower rejects it unless its own log has that entry. On a rejection the leader retries from an earlier entry until the logs agree; the follower then deletes everything after that point and takes the leader's entries. By induction, if two logs hold an entry with the same index and term, they are identical up to that entry.

The commit rule has one subtlety, the case the Raft paper devotes a figure to. An entry is committed once the leader that created it has stored it on a majority, and a leader advances its commit index only by counting replicas of entries from its own term. An entry left over from an earlier term can sit on a majority and still be overwritten by a later leader; it is safe only once a current-term entry after it commits. So a new leader opens its term by appending a no-op entry: committing it commits everything before it and tells the leader where the commit point is. Watch it happen in the Raft simulator, or code the vote count in the Raft election challenge.

05

Reads that don't lie: ReadIndex and leases

Raft's writes are safe by construction: a deposed leader can't get a majority to accept its entries. Its reads are not. A leader cut off by a partition, with a new leader already elected on the other side, still has its local state and can answer from it, missing writes the new leader has committed. It hasn't heard of the new term, so from where it sits nothing is wrong.

Read pathHowLinearizable?Cost
Local readThe leader answers from local stateNoNone
Through the logAppend the read as an entry and answer once it commitsYesA replication round plus a disk write
ReadIndexNote the commit index, confirm leadership with one heartbeat round to a majority, wait until that index is applied, then answerYesOne round trip, no disk write
Lease readAfter a successful heartbeat round, treat leadership as safe for a bit less than the election timeout and answer locallyOnly if clock drift stays boundedNone, but correctness rests on clocks
Follower readThe follower asks the leader for a read index, waits until it has applied that far, then answersYesOne round trip; spreads the load

ReadIndex has one precondition: a new leader must first commit an entry from its own term (the no-op above) before it knows the true commit index. In practice, etcd serves linearizable reads by default using ReadIndex and offers a cheaper serializable read answered by the local member, which can be stale. ZooKeeper answers reads from whichever server the client is connected to, so they can lag; calling sync first makes that server catch up with the leader. CockroachDB serves reads from each range's leaseholder.

06

Changing the cluster: membership and snapshots

Membership changes. Switching from configuration A to configuration B in one step is unsafe. Mid-switch, some nodes count votes against A and some against B, and a majority of each can be disjoint (two of {1, 2, 3} against three of {1, 2, 3, 4, 5}), so two leaders can win the same term. Raft has two safe methods. Joint consensus, from the original paper, passes through an intermediate configuration in which every decision needs majorities of both A and B. Single-server changes, from Diego Ongaro's dissertation, add or remove one node at a time, because then any majority of the old configuration overlaps any majority of the new one. Either way, add a node as a non-voting learner first and promote it once it has caught up, as etcd supports; an empty voter raises the quorum size before it can acknowledge anything.

Snapshots. The log can't grow forever. Each node periodically snapshots its state machine and discards the log up to that point, keeping the index and term of the last entry the snapshot covers, because the next AppendEntries consistency check compares against exactly that entry. A follower so far behind that the leader has already discarded the entries it needs is sent the snapshot itself (InstallSnapshot), then resumes normal replication.

07

Paxos in two phases

Basic Paxos decides one value. Proposers use proposal numbers that are unique and increasing (a counter paired with the proposer's ID works), and a value is chosen once a majority of acceptors accept the same proposal.

StepMessageRule
1aPrepare(n), proposer to acceptorsChoose n higher than any number the proposer has seen
1bPromise(n), acceptor to proposerOnly if n beats every Prepare it has answered: promise to reject proposals numbered below n, and report the highest-numbered proposal already accepted, if any
2aAccept(n, v), proposer to acceptorsNeeds promises from a majority. v must be the value of the highest-numbered proposal they reported; only if none was reported may the proposer use its own
2bAccepted(n, v), acceptor to learnersAccept unless a higher-numbered Prepare has been promised since; once a majority accepts, v is chosen

Rule 2a is the heart of it: once a value might have been chosen, any later proposer is forced to propose that same value, so a different one can never win. Two proposers can still livelock by pre-empting each other with ever-higher numbers, so real systems elect a distinguished leader. Multi-Paxos runs phase 1 once for that leader, then decides a long run of log slots with phase 2 alone, one round trip each. Google's Chubby lock service and Spanner replicate with Paxos; ZooKeeper uses its own protocol, Zab.

Seen this way, Raft is Multi-Paxos with a stricter rule about who may lead. A term plays the proposal number, an election plays phase 1, and AppendEntries plays phase 2. The difference is that a Paxos leader learns which values it must re-propose from the promises it collects, while Raft lets only a node with an up-to-date log win, so its leader never has to learn anything from voters. Heidi Howard and Richard Mortier's 2020 paper "Paxos vs Raft: Have we reached consensus on distributed consensus?" draws the comparison in detail, and the Raft vs Paxos handbook covers the practical choice.

08

Coordination services

You almost never implement consensus yourself. Instead you lean on a coordination service — ZooKeeper, etcd, or a cloud equivalent — which runs the consensus protocol internally and exposes simple primitives: leader election, distributed locks, configuration storage, and membership/failure detection. The rule of thumb: keep the coordination service for the small, critical state that must be globally agreed, and keep your bulk data out of it.

→ Mental model

The senior move is knowing where you genuinely need consensus (one leader, one source of truth) versus where you can avoid it entirely with idempotency and eventual consistency. Consensus is correct but expensive — every decision costs a majority round-trip.

09

Distributed transactions: 2PC vs saga

When one logical operation spans multiple services or shards, you need them to agree on commit-or-abort.

Two-Phase Commit (2PC)

  • Coordinator: "prepare?" → all vote
  • All yes → "commit"; any no → "abort"
  • Strong atomicity across services
  • Blocks if coordinator dies mid-flight
  • Holds locks for the whole round-trip

Saga

  • A sequence of local transactions
  • Each step has a compensating undo
  • Failure → run compensations backwards
  • Non-blocking, eventually consistent
  • You design the rollbacks

2PC buys you atomicity at the cost of availability — a stalled coordinator leaves participants holding locks. Sagas trade atomicity for availability: each step commits independently and a failure triggers compensating actions (refund the charge, release the seat). Most modern microservice systems prefer sagas precisely because they don't block.

10

Idempotency: the exactly-once illusion

Networks deliver at least once — retries mean the same request can arrive twice. True "exactly-once delivery" is impossible, but you can get exactly-once effects by making operations idempotent: applying them twice has the same result as applying them once.

The standard mechanism is an idempotency key — a unique ID the client attaches to a request. The server records processed keys and, on a duplicate, returns the original result instead of doing the work again. This is how a retried payment charges the card once, and how a redelivered queue message isn't processed twice.

Idempotency key deduplicates the retry
Pay req
key=ab12
→
Charged ✓
store ab12
→
Retry
key=ab12
→
Seen ab12 →
return original
→ Key insight

Idempotency is how you avoid needing consensus. If every operation is safe to retry, much of a system can run on cheap at-least-once delivery and eventual consistency — reserving expensive agreement for the few places that truly need one source of truth. Pair it with the fencing tokens from concurrency control to stay correct under retries and pauses.

RUN IT YOURSELF

Count a quorum and run an election

Consensus is majority arithmetic. This computes the fault-tolerance table — and shows why clusters are odd-sized, because a 4-node cluster survives no more failures than a 3-node one, just wasting a vote. Then it runs a Raft leader election: a 3-2 split elects the candidate with the majority, but a 2-2 split (one node crashed) elects no one, forcing a new term. And because two majorities of five sum to six, two candidates can never both win. Change the cluster size or the vote split.

CPython · WebAssembly
Frequently asked

Quick answers

Why do we need Raft or Paxos?

Nodes that can crash or be partitioned still need to agree on one value — the leader, the next log entry, a commit decision. These algorithms let a majority quorum agree safely even when a minority fails.

2PC vs saga?

2PC is an atomic prepare-then-commit across services, but blocks if the coordinator fails. A saga is a chain of local transactions with compensating undos — non-blocking and eventually consistent, but you write the rollbacks.

What is idempotency?

An operation that has the same effect applied once or many times. Using an idempotency key the server deduplicates on, retries give exactly-once effects on top of at-least-once delivery.

Why does consensus need a majority?

Any two majorities of the same set overlap on at least one node, so two conflicting decisions can't both win — even during a partition. That quorum intersection is what keeps consensus safe.

How does Raft elect a leader?

A follower that hears nothing from a leader for a randomized election timeout becomes a candidate, increments its term, votes for itself and asks the others for votes. Each node grants one vote per term, and only to a candidate whose log is at least as up to date as its own. A candidate with votes from a majority becomes leader for that term; a split vote times out and retries in a higher term.

Are reads from a Raft leader linearizable?

Not automatically. A leader that has been deposed without knowing it can still answer from its local state. Linearizable reads need a check: ReadIndex confirms leadership with one heartbeat round to a majority before answering, and lease reads skip that round trip by trusting bounded clock drift. etcd uses ReadIndex for its default linearizable reads.

▶  Watch it explained

Two-Phase Commit: How Databases Agree (and Get Stuck)

Finished this one? 0 / 208 Handbooks done
NextRaft vs Paxos Next on the “Distributed systems, from first principles” ladder · rung 6 of 7

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More Handbooks