Part 3 · 2 chapters · ~20 min
Replication
Single-leader, multi-leader and leaderless replication: who accepts writes, synchronous versus asynchronous followers, failover and fencing, quorums; then replication lag as user-visible anomalies with their fixes, and conflict resolution by last-write-wins, siblings and CRDTs.
7
Leader-follower, multi-leader, leaderless
who accepts writes
- Single-leader: conflict-free ordering, with one machine's worth of write throughput.
- Synchronous or asynchronous followers. Semi-synchronous (one sync standby) is the usual compromise.
- Failover: promote a follower and fence the old leader with an epoch.
- Multi-leader: local writes everywhere, at the price of conflicts.
- Leaderless: W + R > N quorums, with read repair.
- Choose single-leader unless you have a reason you can name.
code
-- Postgres: how far behind is each replica? (run on the primary)
SELECT application_name, state, sync_state,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS bytes_behind,
replay_lag
FROM pg_stat_replication;
-- application_name | state | sync_state | bytes_behind | replay_lag
-- standby-az-b | streaming | sync | 0 | 00:00:00.0012
-- replica-reports | streaming | async | 48213992 | 00:00:07.4 ← reports are 7 s staleTHREE SHAPES OF REPLICATION
single-leader, multi-leader and leaderless: who accepts writes, how they spread, and what can conflict
swipe the figure sideways, or tap expand for full screen
1/6
single-leader
Single-leader (Postgres, MySQL, MongoDB, most managed databases): all writes go to the leader, which streams its change log to followers; followers serve reads. Simple and conflict-free: the leader's order is the order. Its limits: write throughput is one machine's, and a leader far from a user adds latency to every write.
8
Lag, anomalies and conflicts
what users see, and how conflicts resolve
- Read-your-writes: read your own data from the leader, or wait until the replica reaches your write position.
- Monotonic reads: pin each user to one replica, or track the highest position seen.
- Consistent prefix: co-locate causally related writes.
- Last-write-wins silently loses data.
- Siblings keep both versions and merge on read.
- CRDTs build the merge into the data type itself.
code
// read-your-writes with a position token (Postgres LSN) carried in the session
// after a write on the primary:
const { lsn } = await primary.one('SELECT pg_current_wal_lsn() AS lsn');
session.minLsn = lsn;
// on the next read, use a replica only if it has replayed past the user's write
async function readProfile(userId: string, session: Session) {
const r = pickReplica();
const ok = await r.one('SELECT pg_last_wal_replay_lsn() >= $1 AS ok', [session.minLsn]);
return (ok.ok ? r : primary).one('SELECT * FROM profiles WHERE user_id = $1', [userId]);
}the frontend version
Optimistic UI (the React course part 8) is a local read-your-writes guarantee: the client shows its own write immediately, whatever the replicas say. Reconcile it against the server response, not against the next read, which may come from a lagging replica.
REPLICATION LAG AND CONFLICTS
the anomalies users see when reads hit a lagging replica, and the ways conflicting writes are resolved
swipe the figure sideways, or tap expand for full screen
1/6
read your writes
Read-your-writes: a user updates their phone number (written to the leader) and the next page load reads from a lagging follower, showing the old number: the user thinks the save failed. Fix: read a user's own data from the leader for a while after they write it, or read from a replica only once it has caught up to the user's last write position (a log sequence number carried in the session).