Skip to content

    System Design

    System Design: Replication

    Learn why we copy data to several machines, how leader-follower, multi-leader and leaderless replication work, and how lag, failover and split-brain cause trouble.

    By Published 16 min read

    Share

    This lesson is part of System Design Building Blocks: Data, Messaging and Scale. Enroll for free, then sign in, to read it in full.

    What you'll learn: By the end of this lesson you will be able to choose between leader-follower, multi-leader and leaderless replication for a given workload. You will explain synchronous versus asynchronous replication and what each costs. You will describe replication lag and fix a read-your-writes bug. You will explain failover and why split-brain is dangerous.

    Imagine a library with one copy of a popular book. Only one person can read it at a time, and if it is lost in a fire, it is gone. Now the library buys three copies and puts them in different buildings. More people can read at once, readers pick the nearest building, and one fire no longer erases the book.

    But a new problem appears. When the author publishes a corrected edition, someone has to update all three copies. For a while, different buildings hold different versions. Replication in databases is exactly this: keeping copies of the same data on several machines, and dealing with the fact that the copies cannot all change at the same instant.

    The core idea

    Replication means storing the same data on multiple nodes and keeping those copies in sync. A node is one machine running the database. We replicate for three reasons:

    • Availability. If one node dies, another has the data and can take over.
    • Read scale. Many nodes can answer read queries, so read capacity grows with the number of copies.
    • Locality. A copy near the user, say in another continent, answers faster than a copy far away.

    Notice what replication does not do. It does not increase the amount of data you can store, because every node holds the full dataset. That is the job of sharding. It also does not protect you against a bad delete, because the delete is replicated too. For that you need backups, as covered in System Design: Reliability Basics.

    The central difficulty is simple to state: a write must reach several machines, and machines and networks fail and slow down. Every design below is a different answer to the question "who accepts writes, and when is a write considered done?"

    Leader-follower replication

    This is the most common design, used by PostgreSQL, MySQL and many other databases. One node is the leader (also called primary or master). All writes go to it. The other nodes are followers (replicas, secondaries). The leader sends each change to the followers, usually as a stream of changes it also writes to its own log. Followers apply the changes in the same order. Reads can be served by the leader or by any follower.

    Leader-follower replicationApplication to Leader (writes); Leader to Follower 1 (change stream); Leader to Follower 2 (change stream); Application to Follower 1 (reads); Application to Follower 2 (reads); Application to Leader (reads that need the latest)ApplicationApplicationLeader: takes all writesLeadertakes all writesFollower 1Follower 1Follower 2Follower 2writeschange streamchange streamreadsreadsreads that need the latest

    Because only one node accepts writes, there are no write conflicts. The order of changes is decided in one place. That simplicity is why this design is the default choice.

    How the change stream is shipped varies. Many databases ship the write-ahead log (the low-level record of every change) or logical row changes; replaying SQL statements is fragile with non-deterministic functions such as the current time. The PostgreSQL documentation at postgresql.org/docs describes its streaming replication in detail.

    Synchronous versus asynchronous

    When the leader receives a write, when should it tell the client "done"?

    • Synchronous: the leader waits until the follower has confirmed it stored the change. If the leader then dies, that follower has the write. The cost is latency, and a stuck follower can block writes.
    • Asynchronous: the leader confirms to the client immediately and ships the change in the background. Writes are fast and a slow follower does not slow anyone down. The cost is that if the leader dies before the change reaches a follower, that write is lost after failover.
    • Semi-synchronous: one follower is synchronous and the rest are asynchronous. If the synchronous follower becomes slow or fails, another is promoted into that role. At any moment at least two nodes have every acknowledged write, without making every write wait for every follower.
    Mode Write latency Data loss on leader crash When to use Alternative
    Asynchronous Lowest Recent writes may be lost Read-heavy apps that tolerate small loss; cross-region copies Semi-synchronous
    Semi-synchronous Moderate Acknowledged writes survive a single failure Databases that must not lose acknowledged writes Fully synchronous
    Fully synchronous Highest; blocks if a follower stalls None, if all followers confirm Small clusters with strict durability needs Consensus-based systems
    NOTE

    Fully synchronous replication to every follower is rarely used, because one slow or dead follower would halt all writes. Systems that need both durability and availability usually use a majority quorum instead, as in the leaderless section below or a consensus protocol (Advanced: Consensus and Leader Election).

    Replication lag and the bugs it causes

    With asynchronous replication, followers are slightly behind the leader. The delay is called replication lag. It is often milliseconds but can grow to seconds or minutes under heavy load, after a network problem, or while a follower catches up. Reading from a follower means reading data that is a little old. This is eventual consistency: if writes stop, all copies will eventually agree. Three classic anomalies follow, each with a fix.

    Read-your-writes

    A user places an order. The write goes to the leader. The page reloads, the read goes to a lagging follower, and the order is missing. The user thinks it failed and orders again.

    sequenceDiagram participant U as User participant L as Leader participant F as Follower U->>L: place order L-->>U: ok Note over L,F: replication still in flight U->>F: read order list F-->>U: order missing L->>F: apply order U->>F: read order list again F-->>U: order now visible

    Fixes:

    • Read from the leader for data the user has just changed, for example for a short window after their write.
    • Track the position (log sequence number or timestamp) of the user's last write, and only read from a follower that has caught up to it.
    • Route a user's requests consistently to the same replica. This does not guarantee they see their write, but it keeps their view from going backwards while that replica stays up.

    Monotonic reads

    A user refreshes twice. The first read hits a fresh follower and shows a new order; the second hits a stale one and the order vanishes. Time seems to move backwards. Pinning each user to one replica prevents it while that replica stays available.

    Consistent prefix reads

    Two writes happen in order: a question, then its answer. A reader sees the answer without the question, because the writes replicated through different paths. This matters mostly in partitioned systems, where partitions replicate independently. The fix is to send causally related writes through the same partition or use a system that tracks causality.

    These guarantees are called session guarantees and are covered in System Design: CAP and Consistency.

    Failover and split-brain

    When the leader fails, the system must pick a new one. This is failover. It has several steps, and every step can go wrong:

    1. Detect the failure. Usually by missed heartbeats (regular "I'm alive" messages). Too short a timeout causes false alarms; too long means long downtime.
    2. Choose a new leader. Typically the follower with the most up-to-date data.
    3. Reconfigure. Tell clients and the other followers about the new leader.
    4. Handle the old leader. If it comes back, it must realise it is no longer in charge and become a follower.
    stateDiagram-v2 [*] --> Healthy Healthy --> Suspected: heartbeats missed Suspected --> Healthy: leader responds again Suspected --> Electing: timeout reached Electing --> NewLeader: follower promoted NewLeader --> Healthy: old leader rejoins as follower

    The dangers:

    • Lost writes. With asynchronous replication, the new leader may be missing the last few writes of the old one. If the old leader returns with them, they conflict or are discarded. Discarding acknowledged writes can do real harm: if the new leader reuses an auto-incrementing ID already handed to another system, data from two users can get mixed up.
    • Split-brain. Two nodes both believe they are the leader and both accept writes. The copies diverge and merging them may be impossible. This can happen when a network partition makes the old leader look dead while it is actually alive.
    • Wrong timeouts. A slow but healthy leader gets replaced during a load spike, and the failover itself adds more load.

    Defences against split-brain include: requiring a majority of nodes to agree on the leader (a quorum, so two groups cannot both reach majority), using leases (a leader's authority expires unless renewed), and fencing (the storage or followers reject requests from an old leader using an increasing token). Tools such as etcd and ZooKeeper exist largely to provide this agreement reliably.

    WARNING

    Automatic failover is a feature you must test, not assume. Many teams discover its edge cases in production. Rehearse failover in a staging environment and measure how long it takes and what it loses.

    Multi-leader replication

    In multi-leader (or multi-primary) replication, several nodes accept writes, and each forwards its changes to the others. The usual reason is geography: a leader in each of Asia, Europe and the Americas lets users write locally, and a regional outage does not stop the other regions. Offline-capable apps, such as a calendar syncing between phone and laptop, are another example: every device is effectively a leader.

    The price is write conflicts. Two users edit the same record in different regions at the same moment, both writes are accepted locally, and the system must decide the result:

    • Last write wins (LWW): keep the later timestamp. Simple, but it silently throws away the other write, and clock differences make "later" unreliable.
    • Merge: combine the changes, as collaborative editors and conflict-free replicated data types (CRDTs) do.
    • Application resolution: store both versions and let the application or the user decide.
    • Avoid conflicts: route all writes for a record to one "home" region, which removes most conflicts by design.

    Uniqueness constraints and auto-increment IDs do not work across leaders without extra care. Use multi-leader only when you truly need local writes in several places or offline operation.

    Leaderless replication and quorums

    In leaderless designs, such as Dynamo-style systems like Cassandra, no node is special. A client (or a coordinator node) sends each write to several replicas and reads from several replicas.

    Let N be the number of replicas holding a piece of data, W the number that must acknowledge a write, and R the number that must answer a read. If R + W > N, every read set overlaps every write set in at least one node, so a read will include at least one replica that saw the latest successful write.

    For example, with N = 3, W = 2 and R = 2: a write succeeds when two of three replicas confirm, a read asks two of three, and at least one node in the read has the newest value, which the client picks by version. You can tune the numbers: W = 1 and R = 3 favours fast writes, W = 3 and R = 1 favours fast reads, and each reduces availability for the expensive operation.

    Two cautions keep this honest:

    • Overlap alone does not give the strict guarantee of a single leader. Concurrent writes, sloppy quorums (writing to other nodes temporarily when the usual ones are unreachable), partial write failures and clock-based conflict resolution can all produce stale or lost values. Treat quorums as "better odds", not as a transaction.
    • Replicas that missed writes are repaired by read repair (fixing stale copies when a read notices a mismatch) and background anti-entropy processes.

    Choosing a replication design

    Design Strength Cost Use when Alternative
    Leader-follower Simple, no write conflicts, strong tooling One write node; lag on followers; failover is delicate Most applications, especially read-heavy ones Add caching; shard if writes outgrow one node
    Multi-leader Local writes in many regions; works offline Conflict resolution; weak constraints Multi-region writes, offline sync Single leader plus read replicas near users
    Leaderless (quorum) No failover step; tolerates node loss smoothly Tuning, stale reads, conflict handling Very high availability, large-scale key-value workloads Leader-follower with a consensus layer

    The same choice as a decision flow:

    flowchart TD S["Choosing a replication design"] --> Q1{"Must writes be accepted in several regions or offline?"} Q1 -->|yes| Q2{"Can each record be routed to one home region?"} Q2 -->|yes| R1["Leader-follower per region with home-region routing"] Q2 -->|no| R2["Multi-leader with an explicit conflict policy"] Q1 -->|no| Q3{"Need no failover step and can tolerate stale reads?"} Q3 -->|yes| R3["Leaderless quorum store"] Q3 -->|no| R4["Leader-follower, semi-synchronous"]

    Most teams land on the bottom right, and the diagram shows why: the other exits each require a stated need. A sensible start for most products is one leader with two or three followers in different availability zones, semi-synchronous replication, non-critical reads (reports, catalogues) served by followers, and "the thing I just changed" served by the leader or a caught-up follower. Consider multi-region writes only if latency or regulations demand them.

    In practice: moving reports to read replicas, then "my order disappeared"

    NOTE

    A realistic scenario, not a case history. This is a composite example built from common production patterns. It does not describe any specific company, and every number in it is an illustrative assumption.

    The situation

    A B2B ordering SaaS serves 2,000 wholesale customers, who place orders through a web app and read monthly sales reports. A team of nine runs a Spring Boot application on Kubernetes against a managed PostgreSQL primary. Month-end report runs are heavy aggregate queries, and at 09:00 on the first working day the primary hits 90% CPU and order placement slows from about 200 ms to over 2 seconds (illustrative). The team adds a read replica and sends reports to it. It works, so someone flips a framework setting to send every GET to the replica.

    Within a week support logs "my order disappeared" tickets. A customer places an order, is redirected to the order list, and sees nothing, so some place it again. Assume duplicate orders reach about 0.5% of orders on busy days. Replica lag is normally under a second but rises to 20 seconds or more while the report queries run.

    Starting architecture

    Order database, starting architectureApplication to PostgreSQL primary (writes); Application to PostgreSQL replica 1 (all GETs); PostgreSQL primary to PostgreSQL replica 1 (async streaming); PostgreSQL primary to PostgreSQL standby (async)Application: 6 podsApplication6 podsPostgreSQL primary: all writesPostgreSQLprimaryall writesPostgreSQL replica 1: GETs and reportsPostgreSQLreplica 1GETs and reportsPostgreSQL standby: failover copyPostgreSQLstandbyfailover copywritesall GETsasync streamingasync

    The replica that serves customers is also the one running multi-minute report queries, so reads and reports share a lag problem.

    Options considered

    Option What it fixes What it costs Risk
    A. Smaller change: reports only go to the replica, all application reads return to the primary The disappearing orders, at once A config change Primary read load comes back, though without the report spike
    B. A plus read-your-writes routing: after a write, a session reads from the primary for a short window or until a replica has replayed its write position Keeps most reads off the primary without the bug One to two weeks of routing code Subtle edge cases; a fallback can overload the primary
    C. Move reporting to a warehouse fed by nightly extracts Removes reports from OLTP entirely Six or more weeks, a pipeline to own Reports are a day old

    The decision

    Context Reports are heavy but infrequent; order pages need to be correct immediately after a write
    Decision Option A as a same-day hotfix, then B for the order list and detail pages. Reports get a dedicated replica, separate from the failover standby
    Consequences, good The bug stops; the primary is protected from report queries; lag on the report replica no longer matters to customers
    Consequences, bad More replicas to run; routing logic in the application; a few reads go to the primary on purpose
    Revisit when Reports keep the report replica above about 70% CPU, or customers need reports fresher than a replica can safely give, so a warehouse becomes worth it

    Rollout and verification

    Ship the routing as a small decision made once per request:

    Read routing decision per requestIncoming request to Is it a write?; Is it a write? to Wrote in the last 10 s? (no); Wrote in the last 10 s? to Replica lag under 5 s? (no); Is it a write? to Primary (yes); Wrote in the last 10 s? to Primary (yes); Replica lag under 5 s? to Primary (no); Replica lag under 5 s? to Replica (yes)Incoming requestIncomingrequestIs it a write?Is it a write?Wrote in the last 10 s?: same sessionWrote in thelast 10 s?same sessionReplica lag under 5 s?Replica lagunder 5 s?Primary: capped fallbackPrimarycapped fallbackReplicaReplicanonoyesyesnoyes

    Release it behind a flag for 10% of sessions first. Before that, replay a recorded month-end load against staging with reports running, and check that lag alarms fire. Dashboards that prove it: replica lag (replay_lag from pg_stat_replication on the primary), the percentage of reads routed to the primary and why, report duration, primary CPU, and the rate of repeated order submissions per customer, which is the user-visible symptom. A target of zero "disappeared order" tickets over two month-ends is the real test.

    The 2 a.m. failure

    At month end, the page reads "Replica lag above 30 s" and soon after "Primary CPU above 90%". A long report is delaying replay on the replica (on a PostgreSQL standby, replay waits for conflicting queries up to a configured limit, which had been raised for reports), the lag guard sends reads to the primary, and the primary becomes the next bottleneck.

    sequenceDiagram participant A as App participant R as Replica participant P as Primary A->>R: long report query Note over R: replay delayed, lag grows A->>P: reads rerouted, lag over 5 s Note over P: CPU saturates A->>P: order writes slow down Note over R: operator cancels the report, lag drains

    First three checks: on the primary, SELECT client_addr, state, replay_lag FROM pg_stat_replication;; on the replica, SELECT pid, now() - query_start AS age, left(query, 60) FROM pg_stat_activity WHERE state = 'active' ORDER BY age DESC LIMIT 5;; and the dashboard panel showing the share of reads routed to the primary. Mitigation: cancel the offending report with pg_cancel_backend(pid), confirm lag drains, and pause scheduled report generation until traffic calms. Follow-up: a statement timeout on report connections, reports on their own replica (as decided), a cap on how much fallback traffic the primary accepts, and report schedules moved away from the 09:00 order peak.

    Architect's notes

    • Measure lag, not just whether the replica is up. Alert on replay lag in seconds and on the oldest long-running query on each replica.
    • Ask in the review: "Which reads must see the user's own write?" List them. The answer is usually shorter than people fear.
    • Do not route by HTTP method alone. A GET right after a POST is the classic trap, so write the exceptions into the design.
    • Separate the failover replica from the reporting replica. A standby that analysts can saturate is not a standby.
    • Fallbacks need their own limits. A guard that sends all reads to the primary moves the outage rather than preventing it.
    • Defer multi-region writes. Cross-region read replicas answer most locality needs at a fraction of the conflict risk.

    Common mistakes

    • Treating replicas as backups. Deletes and corruption replicate instantly. Keep independent, point-in-time backups and test restores.
    • Reading from followers without handling lag. Users see their own changes vanish. Use read-your-writes routing or read from the leader for fresh data.
    • Assuming asynchronous replication loses nothing. Acknowledged writes can disappear on failover. Decide your acceptable loss (your RPO) and choose semi-synchronous replication if it is near zero.
    • Never rehearsing failover. Untested failover breaks in surprising ways. Run drills and time them.
    • Replicating but putting all copies in one zone. One power or network failure still takes everything down. Spread nodes across availability zones.
    • Adopting multi-leader to "scale writes". It adds conflicts without increasing total write capacity for a single record. If writes outgrow a node, partition the data instead.
    • Using last-write-wins casually. It drops data silently, and clock skew picks the wrong winner. Choose conflict handling on purpose.

    Key takeaways

    • Replication copies data for availability, read scale and locality. It does not add storage capacity and it is not a backup.
    • Leader-follower is the default. One write node means no conflicts and a clear order of changes.
    • Synchronous trades latency for durability. Asynchronous trades possible data loss for speed; semi-synchronous sits in between.
    • Lag creates visible bugs. Read-your-writes, monotonic reads and consistent prefix are the guarantees users notice.
    • Failover is the risky moment. Detection, election, reconfiguration and old-leader handling can each fail.
    • Split-brain needs majority, leases or fencing. Never rely on timeouts alone to decide who is leader.
    • Multi-leader means conflicts. Prefer routing writes to one home region unless you truly need otherwise.
    • Quorums improve odds, not certainty. R + W > N creates overlap, but sloppy quorums and concurrency still allow anomalies.

    Practice exercises

    Exercise 1 — Starter (30 min): A leader-follower database has one leader and two followers. For each of three modes (asynchronous, semi-synchronous, fully synchronous), write down what a client sees when the leader crashes immediately after acknowledging a write, and whether that write survives. Present it as a table.

    Exercise 2 — Intermediate (2 hours): Design the read path for the photo app's profile page. List the five pieces of data on the page (own bio, photo grid, follower count, other users' recent comments, notification badge). For each, decide whether it may be read from a lagging follower, must be read from the leader, or needs a caught-up follower. Explain how your application would implement read-your-writes using a stored write position.

    Exercise 3 — Advanced (half day): A leaderless store uses N = 5. Compute, for W and R from 1 to 5, which combinations satisfy R + W > N, and how many node failures each tolerates for reads and for writes. Then describe one concrete scenario (sloppy quorum or concurrent writes) where even a combination satisfying R + W > N returns a stale value, and propose a detection or repair mechanism.

    Enroll to keep reading

    Lesson 2 of 14 · System Design Building Blocks: Data, Messaging and Scale

    Learn why we copy data to several machines, how leader-follower, multi-leader and leaderless replication work, and how lag, failover and split-brain cause trouble.

    In the rest of this lesson

    • Failover and split-brain
    • Multi-leader replication
    • Leaderless replication and quorums
    • Choosing a replication design
    • In practice: moving reports to read replicas, then "my order disappeared"
    • Architect's notes

    Enrolling is free. It unlocks every lesson in System Design Building Blocks: Data, Messaging and Scale, tracks your progress across devices and keeps your quiz scores. The newsletter is optional.

    Ask about this article

    Get answers grounded in this post. AI-generated — based on this article, and may be imperfect.

    Scaled AI Weekly

    Enjoyed this? Get more like it every Monday.

    Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.

    Get new articles by email

    Free. We email a confirmation link first, and you can unsubscribe from any email.

    Join engineers building production AI systems

    Comments