Replication
Leaders and followers: when is a write done?
leader-followersync-replicationsemi-sync-replicationasync-replicationreplication-logcatch-up-recoveryLeader streams its log to followers
t = 0.0 sSquares are log entries on their way to followers; green dots are follower acknowledgements. Click a follower to take it down or bring it back.
Replication logs, aligned by offset
Try: in asynchronous mode, raise the lag to 1.5 s and crash the leader. The acknowledged writes the new leader never received are lost (✕). Switch to synchronous and nothing acknowledged is lost, but take one follower down (click C) and every write blocks. Semi-sync keeps both properties as long as one follower is healthy.
Asynchronous replication with automatic failover
t = 0.0 sClick a downed node to restart it. A restarted old leader still believes it is the leader.
Logs: entries colored by the epoch of the leader that accepted them
Event log
- Press Play, then kill or freeze the leader.
Try: freeze the leader for 2.5 s with a 1.5 s timeout. The followers give up on it and elect a new leader, even though nothing crashed. The old leader wakes up still thinking it is in charge, and with fencing off clients keep writing to it (red entries): split brain. Turn fencing on and repeat: it sees the newer epoch, steps down, and throws away its unreplicated writes. Raise the timeout above the freeze and no failover happens, but a real crash then takes longer to recover from.
Replication lag: your post vanished, and time ran backwards
replication-lagread-your-writesmonotonic-readsSpace-time diagram (last 10 s)
t = 0.0 sTry: with “any follower”, about half the reloads land on the slow follower 2 and your post seems to disappear, or the page shows fewer comments than a moment ago. “Read-your-writes” fixes the first problem only. “Monotonic” pins you to follower 2, so you never go back in time, but you still miss your own post until it catches up. “Both” tracks the newest position you have written or seen, and only replicas at least that far along serve your reads.
Consistent prefix reads: the answer before the question
consistent-prefixTwo partitions, each with its own leader and follower
What the observer sees at 5.0 s
Try: drag the playhead into the red band. The P2 follower is fast and the P1 follower is slow, so the observer sees the answer first. Lower the P1 lag below the gap between question and answer and the anomaly disappears without any fix. That is the danger: it only appears under load. Switch to “same partition” and the single follower log keeps the writes in order whatever the lag.
Two datacenters, a leader in each
convergedEvent log
- Press “Both at once” to write concurrently in both DCs.
Try: “Both at once” with arrival order. Each DC applies the other's write last, so DC 1 ends at C and DC 2 at B, and they stay different forever. Switch to LWW and they converge, but one write is silently dropped. Now set the DC 2 clock to −1.5 s, let user 1 write, and have user 2 write a moment later: LWW keeps the older write. Merge and siblings lose nothing. Cut the link, write on both sides, then restore it to see offline edits conflict.
One key, n home replicas (click a node to make it unreachable)
Green ring = holds the latest successful write (v1). Red ring = stale. White ring = in the last read set. Yellow lines = writes accepted by nodes outside the key's home set.
Event log
- Take a node down, write, bring it back, then read.
Try: with n=3, w=2, r=2, click N3 to take it down, write v2, bring N3 back. N3 is stale, but every 2-node read set includes a fresh node, and read repair fixes N3 when it gets read. Now set r=1: one read in three hits the stale node. Then set r=2, turn sloppy quorum on, take N2 and N3 down, and write. X1 accepts the write on N2's behalf, so w is met. Bring N2 and N3 back and take N1 down: every read is stale, even though w + r > n, until you run the hinted handoff.
Client 1
talks to AClient 2
talks to ADatabase
Causality: which write overwrote which
Try: step through Figure 5-13. Each client sends the version it last read. The server overwrites exactly the values that version covers and keeps the others as concurrent siblings, so after five writes nothing is lost. Switch to last write wins and repeat: the clients overwrite each other and items vanish. With two replicas, each client writes to its own replica, and the conflict only appears once you sync them.
- takeawayA single leader gives one write order and no conflicts. You still choose between waiting for followers (durable but fragile) and not waiting (fast, but acknowledged writes can be lost on failover). Failover needs a good timeout and fencing to avoid split brain.
- takeawayFollowers are eventually consistent, with no bound on the lag. Decide which guarantees your users need (read-your-writes, monotonic reads, consistent prefix) and route reads accordingly. Don't pretend replication is synchronous.
- takeawayMulti-leader and leaderless systems tolerate failures and latency, but concurrent writes are a normal event for them. They need deterministic conflict resolution and causality tracking (version vectors), or they will silently lose data, as LWW does.
Key concepts · 21
book chapter 5- Single-leader replication
One replica (the leader) accepts all writes and streams them, in order, to read-only followers.
There is a single order of writes, so there are no write conflicts. The cost is that every write goes through one node, and reads from followers may be out of date.
PostgreSQLMySQLMongoDBKafka↑ see it in the visualization - Synchronous replication
The leader confirms a write to the client only after a follower has also confirmed it.
An acknowledged write survives the loss of the leader. But one slow or failed follower blocks every write, so a fully synchronous setup is fragile.
↑ see it in the visualization - Semi-synchronous replication
One follower is synchronous and the rest are asynchronous. If the synchronous one becomes slow, an asynchronous follower takes its place.
This guarantees an up-to-date copy on at least two nodes without letting a single straggler stop all writes.
MySQL semi-syncPostgreSQL synchronous_standby_names↑ see it in the visualization - Asynchronous replication
The leader acknowledges a write as soon as it is stored locally and sends it to followers in the background.
Writes are fast and keep working when followers fail. However, writes that were acknowledged but not yet replicated are lost if the leader dies.
↑ see it in the visualization - Replication log
The stream of changes a leader sends to its followers. It can be statement-based, write-ahead-log shipping, a logical (row-based) log, or trigger-based.
Replicating statements breaks on nondeterminism (NOW(), RAND()). Shipping the WAL ties followers to the leader's storage format. A logical row log is decoupled from the storage engine and can feed change data capture.
MySQL binlogPostgreSQL WALDebezium↑ see it in the visualization - Follower catch-up
A follower that restarts or falls behind asks the leader for every change after the last log position it processed.
Because the log has positions, a follower that was down can recover without a full copy. New followers start from a consistent snapshot and then catch up the same way.
↑ see it in the visualization - Failover
When the leader fails, the system detects it (usually with a timeout), promotes the most up-to-date follower, and redirects clients and other followers to it.
If the timeout is too short, the system fails over when nothing is wrong. If it is too long, recovery is slow. With async replication, writes the old leader had not replicated are thrown away.
↑ see it in the visualization - Split brain
Two nodes both believe they are the leader and both accept writes.
Their data diverges and there is no automatic way to reconcile it. Systems use fencing (STONITH, epoch numbers) to shut down or reject the deposed leader.
↑ see it in the visualization - Replication lag & eventual consistency
The delay between a write on the leader and that write appearing on a follower. In normal operation it is under a second, but under load or during failures it can be minutes.
Asynchronous followers converge only eventually, and the delay has no upper bound. Applications that read from followers have to decide which anomalies they can tolerate.
↑ see it in the visualization - Read-your-writes consistency
A user always sees their own updates after a page reload. Other users' updates may still be delayed.
Without it, a user who has just posted something reloads the page and the post seems to be gone. Fixes include reading recently modified data from the leader, or tracking the user's last write position and serving reads only from replicas that have caught up to it.
Facebook's read-from-leader window↑ see it in the visualization - Monotonic reads
If a user has read a value, later reads never return an older one.
Reads that hit a fresh replica and then a stale one make time appear to move backwards. Pinning each user to one replica (for example by a hash of the user ID) prevents this.
↑ see it in the visualization - Consistent prefix reads
Anyone reading a sequence of writes sees them in the order they happened.
In a partitioned database, partitions replicate independently, so an answer can show up before its question. Putting causally related writes in the same partition, or tracking their dependencies, fixes it.
↑ see it in the visualization - Multi-leader replication
Several nodes accept writes (for example one leader per datacenter) and exchange their changes asynchronously.
Writes are local in each datacenter and the system survives a datacenter or network outage. In exchange, the same record can be changed concurrently in two places, and those conflicts have to be resolved.
MySQL TungstenBDR for PostgreSQLCouchDBGoogle Docs↑ see it in the visualization - Conflict resolution & convergence
A way to make every replica reach the same final value after concurrent writes: last write wins, replica priority, merging values, or keeping all versions for the application or user to resolve.
If each replica simply applies writes in the order they arrive, replicas end up permanently different. Any automatic rule has to be deterministic so that all replicas reach the same result.
CRDTsBucardo custom handlersRiak siblings↑ see it in the visualization - Last write wins (LWW)
Each write carries a timestamp, the highest one is kept, and the others are silently discarded.
Replicas converge, but at the cost of durability: concurrent writes are lost, and with clock skew even a write that really happened later can lose.
CassandraRiak (optional)↑ see it in the visualization - Leaderless replication
The client (or a coordinator) sends every write and every read to several replicas in parallel. No node is in charge of ordering writes.
There is no failover, and a few slow or dead nodes are tolerated. Stale replicas are repaired by readers and by background processes.
DynamoCassandraRiakVoldemort↑ see it in the visualization - Quorum reads and writes (w + r > n)
With n replicas, a write must be confirmed by w of them and a read must query r of them. If w + r > n, every read set overlaps every write set.
Overlap means at least one queried replica has the latest successful write. There are still edge cases: sloppy quorums, concurrent writes, failed writes that are not rolled back, and a node restored from a stale replica.
↑ see it in the visualization - Read repair & anti-entropy
Read repair: a client that sees a stale response writes the newer value back to that replica. Anti-entropy: a background process compares replicas and copies the missing data.
Read repair only fixes values that get read. Without anti-entropy, rarely read keys can stay stale on some replicas indefinitely.
↑ see it in the visualization - Sloppy quorum & hinted handoff
When a key's home nodes are unreachable, writes are accepted by other nodes. Those nodes hand the data back to the home nodes once they recover.
Writes stay available during network problems, but w + r > n no longer guarantees a fresh read, because the w writes may land outside the n home nodes.
RiakCassandra hinted handoffVoldemort↑ see it in the visualization - Happens-before & concurrency
Operation B happens after A if B knew about A, depended on it, or built on it. If neither knew about the other, the two are concurrent, whatever their wall-clock times.
When one operation happened before the other, the later one may overwrite the earlier. When they are concurrent, there is a real conflict and both values have to be kept or merged.
↑ see it in the visualization - Version vectors & siblings
One version counter per replica, sent with every read and write. The server overwrites the values a write has seen and keeps concurrent values as siblings.
This tells overwrites apart from concurrent writes without depending on clocks, so no data is silently dropped. The application merges siblings, for example by taking the union of shopping carts (deletions need tombstones).
Riak dotted version vectorsVoldemort↑ see it in the visualization