Knowledge Guide
HomeSystem DesignLeader and Follower

Leader/Follower — Replica-Lag Anomalies, Failover Data Loss, Log-Shipping & Raft Leader Completeness (Deep Dive)

Why the basic leader/follower story isn’t enough

A follower is not a live copy of the leader — it is a replay of the leader’s log running behind it by some lag ℓ. Almost every surprising bug in a leader/follower system, from a user not seeing their own write to a full-blown split-brain, is that one fact showing up somewhere you didn’t expect it. This page covers the gaps the base pattern lesson and the Raft mechanics lesson leave open: what lag ℓ does to reads, what happens when failover is a script instead of a vote, what “acked” really means under async replication, why Raft’s safety guarantee is a forced consequence rather than an assumption, and how the log itself gets shipped.

Reading from followers under replica lag: three anomalies

Every anomaly below reduces to the same mechanism: a follower's state at wall-clock time t is the leader's state at time t−ℓ. Route a read to the wrong follower at the wrong moment and you see the past — the only question is whose past, and whether it's consistent with what you saw a moment ago.

tEventFollower F (lag 400ms)Follower G (lag 50ms)
t=0Client writes profile.bio = "Senior Engineer" (LSN 102) to the leader; leader acks.still at LSN 101 (“Engineer”)still at LSN 101
t=50msSame client re-reads its own profile; load balancer routes to F.returns “Engineer” — the value before the client's own write
t=100msClient refreshes again; routed to G this time.caught up — returns “Senior Engineer”
t=150msClient refreshes again; routed back to F.still lagging — returns “Engineer” again

Two distinct violations are visible in that single trace:

A third anomaly needs two causally related writes rather than one: consistent-prefix violation. Say a user posts a comment (write C1) and then replies to it (write R1); if C1 and R1 land on different shards/partitions with independent lag, a reader can observe R1 (“reply to C1”) on a shard that's caught up while C1's shard is still behind — seeing the effect (the reply) before its cause (the comment) exists at all.

Mitigations

Split-brain under externally-orchestrated failover

The base pattern lesson already distinguishes self-organizing consensus (Raft-style, term-based) from externally-orchestrated failover (Patroni, Orchestrator, MHA). The second case has no term/epoch of its own — the orchestrator watches health and issues a promote command, and a promote command creates a new leader without necessarily silencing the old one. That gap is where split-brain lives.

  1. Leader L1 hits a long GC pause (or a network blip). It is not dead — it's alive, holding its socket — but it stops answering health checks.
  2. The orchestrator's health check to L1 times out three times in a row and declares it dead. It promotes follower F2, which starts accepting writes as the new leader.
  3. L1's GC pause ends a moment later. It resumes exactly where it left off. Because there's no consensus term for it to check against, it has no way to know a new leader now exists — and it keeps accepting writes from any client whose connection pool or stale DNS entry still points at it.
  4. Now two nodes, L1 and F2, both believe they are the leader and both accept writes. Two clients writing the “same” record diverge with no reconciliation rule — classic dual-primary split-brain.

The root cause: “detected as dead” and “actually stopped” are not the same event once there's no vote/term to enforce it. Two fencing mechanisms close that gap:

Without one of these, a promote command is a wish, not a guarantee.

Failover data loss and log divergence

Under asynchronous replication, “acked to the client” and “durable on a majority” are two different events — the leader can ack a write the instant it appends to its own log, before any follower has seen it. That gap is exactly where failover data loss lives, and it produces a second problem on top: a diverged log that has to be reconciled, not merged, when the old leader comes back.

Loss: leader L1's log holds LSN 100 (replicated), then 101 (qty=5) and 102 (qty=3) — both acked to their clients the moment L1 wrote them locally, before either reached follower F, whose replicated position is still 100. L1 crashes. F is promoted; its log tops out at 100. LSNs 101 and 102 are gone — not delayed, gone — because they were never durable anywhere but L1's now-dead disk. The client was told “success” for writes that no longer exist in the system.

Timeline showing an async leader crashing with unreplicated writes, a promoted follower diverging, and the rejoining old leader truncating its tail instead of replaying it
Timeline showing an async leader crashing with unreplicated writes, a promoted follower diverging, and the rejoining old leader truncating its tail instead of replaying it

Rejoining and divergence: L1 restarts and rejoins as a follower. It still has its own 101 (qty=5) and 102 (qty=3) sitting in its local log — entries the new leader F never saw. Meanwhile F, since being promoted, has taken its own fresh writes at those same positions: 101′ (qty=8), 102′ (qty=2). Same LSNs, two different entries — the logs have diverged at position 101.

L1 cannot simply keep its own 101/102 and append F's on top — that would create two different, simultaneously “true” histories of what happened at position 101, which defeats the entire reason leader/follower exists: a single, unambiguous order of writes. Instead L1 must truncate — discard its own divergent tail back to the last point of agreement (LSN 100) — and then replay F's log forward from there. Its own acked-but-unreplicated 101/102 are not merged in; they are deleted. Why not replay them instead of discarding? Because F's log is the one with quorum backing going forward, while L1's extra entries were never acknowledged by a majority — from the system's point of view they never durably happened. Replaying them would silently splice un-quorumed history into a quorumed log, which is exactly the conflict leader/follower replication exists to prevent in the first place.

Raft Leader Completeness — derived, not assumed

The Raft mechanics lesson states that “the election rule guarantees a new leader already has [every committed entry]” — here is why that's true, not just that it is. The formal property (Ongaro & Ousterhout, In Search of an Understandable Consensus Algorithm): if an entry is committed in a given term, it is present in the logs of every leader of every higher-numbered term. It falls directly out of two rules Raft already has for other reasons, stacked together:

  1. The up-to-date check in RequestVote. A follower grants its vote to a candidate only if the candidate's log is at least as up to date as its own — compared first by the term of the last log entry, then, if those tie, by log length. A voter with a “better” log refuses to vote for a candidate with a “worse” one.
  2. Quorum overlap. Both committing an entry and winning an election require a majority of the same N nodes. Any two majorities drawn from the same N nodes must share at least one node — there just aren't enough nodes left for two disjoint majorities to exist.
Two overlapping 5-node majorities for commit and election intersect at exactly one node, which forces the new leader to already hold the committed entry
Two overlapping 5-node majorities for commit and election intersect at exactly one node, which forces the new leader to already hold the committed entry

Traced: the majority-intersection argument

Take N=5 nodes {A, B, C, D, E}.

  1. Entry X is committed in term 3: term-3's leader (A) appended X and replicated it to a majority — say {A, B, C} (3 of 5). “Committed” means exactly this: durable on a majority.
  2. Later, a term-4 election happens. Any winning candidate needs votes from a majority — say candidate D wins with votes from {C, D, E} (3 of 5).
  3. Both {A, B, C} and {C, D, E} are 3-of-5 subsets of the same 5 nodes. By the pigeonhole principle (3 + 3 − 5 = 1), they must share at least one node. Here that node is C.
  4. C is in both majorities: C has X in its log (from step 1, as one of the committers) and C voted for D (from step 2, as one of the electors).
  5. But rule 1 (the up-to-date check) says C only grants its vote to D if D's log is at least as up to date as C's own. C's log contains X. For D's log to be “at least as up to date” as one that already contains X, D's log must also contain X — a candidate missing a committed entry is by definition less up to date than a voter who has it, so that voter would refuse it.
  6. Therefore D, the new term-4 leader, is guaranteed to already have X. It never had to be told about it, discover it, or reconstruct it — the guarantee is a forced consequence of “commit needs a majority” + “election needs a majority” + “voters refuse a worse log,” not an extra mechanism bolted on top.

This is also why the log-divergence rollback from the previous section only ever runs in one direction. A rejoining old leader or follower can have extra, uncommitted entries that must be truncated — but a newly elected Raft leader can never be missing a committed entry, so it never needs the reverse repair.

Log-shipping mechanisms: a design axis

How the leader ships the log to a follower decides what a follower actually is — a re-execution of the same commands, a byte-for-byte copy of the storage engine's internals, or an independent database applying row-level deltas. Three mechanisms, in increasing order of decoupling from the leader's storage engine (Kleppmann, DDIA, ch. 5):

When each is right: statement-based only if every statement in the workload is provably deterministic (rare enough that it's mostly a historical footnote today); WAL/physical for a same-engine, same-version HA pair — the cheapest, most faithful option when that's all you need; logical/row-based the moment you need a different consumer on the other end — CDC, an analytics replica, a heterogeneous database, or an upgrade you can't afford to do as a hard cutover.

Pitfalls

Judgment layer

Synchronous vs. asynchronous replication

Synchronous replication guarantees no acked write is ever lost on failover, at the cost of write latency including a round trip to at least one follower and reduced write availability — a slow or unreachable follower stalls every write. Asynchronous replication gives low, predictable write latency at the cost of a data-loss window exactly the size of “whatever was acked but not yet replicated” when the leader dies. A named alternative that sits between the two: Raft-style consensus with a quorum commit — commit is majority-durable (so nothing acked is ever lost) without waiting on one specific, fixed follower (any majority will do, so one slow node doesn't stall writes) — at the cost of running a full consensus protocol (leader election, term bookkeeping) instead of a single leader-to-follower ack.

Which log-shipping mode

Default to WAL/physical for a straightforward same-engine HA pair — it's the cheapest and most faithful option when the follower is just a hot standby of the same engine and version. Move to logical/row-based the moment you need a different kind of consumer on the other end: CDC into an event stream, an analytics-only replica, or a major-version upgrade you need to do without a hard cutover. Avoid statement-based unless every statement in the workload has been specifically audited for determinism — the failure mode (silent divergence) has no error to catch it.

Takeaways


Re-authored/Deepened for this guide, drawing on Martin Kleppmann's Designing Data-Intensive Applications (Ch. 5, “Replication”) and Diego Ongaro & John Ousterhout, “In Search of an Understandable Consensus Algorithm” (Raft, 2014); diagrams hand-authored (SVG) for this guide.

🤖 Don't fully get this? Learn it with Claude

Stuck on Leader/Follower — Replica-Lag Anomalies, Failover Data Loss, Log-Shipping & Raft Leader Completeness (Deep Dive)? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Leader/Follower — Replica-Lag Anomalies, Failover Data Loss, Log-Shipping & Raft Leader Completeness (Deep Dive)** (System Design) and want to truly understand it. Explain Leader/Follower — Replica-Lag Anomalies, Failover Data Loss, Log-Shipping & Raft Leader Completeness (Deep Dive) from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Leader/Follower — Replica-Lag Anomalies, Failover Data Loss, Log-Shipping & Raft Leader Completeness (Deep Dive)** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Leader/Follower — Replica-Lag Anomalies, Failover Data Loss, Log-Shipping & Raft Leader Completeness (Deep Dive)** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Leader/Follower — Replica-Lag Anomalies, Failover Data Loss, Log-Shipping & Raft Leader Completeness (Deep Dive)** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes