Skip to content

Fundamentals

Replication

How copies of data on several nodes stay in sync, from leaders and followers to replication lag, failover, conflicts, and quorums.

Concise article — AI can help

Copy this prompt and paste it into your AI chat:

Help me study this article for practical system design.
Article: https://tech0ver.github.io/sysdes/fundamentals/replication.md
Ask my goal and preferred depth: a quick recap, a walkthrough, or a deeper discussion. Use the article examples, flag your additions, and let me set the pace.

Or give your AI the article link with your own prompt:

Almost every system design keeps copies of its data on several nodes, and a change takes time to reach every copy. To defend a design, you need to know who accepts writes, when a write counts as done, what a read may see while the copies catch up, and what a failure can lose.

An online store keeps its catalog, customer accounts, orders, and carts in one database. Shoppers browse far more than they buy, so most requests read products and prices. When this node fails, the store stops. While it works, every read from every country lands on this one machine.

Replication keeps a copy of the same data on several nodes. Each copy is a replica. Replicas let the data survive the loss of a node, spread reads over many machines, and put a copy close to the users who read it. The hard part of replication is keeping the copies in step while the data changes.

The most common design has one leader and several followers. All writes go to the leader. It applies each write and sends the change to every follower through the replication log, a stream of changes in the order the leader applied them. Each follower applies the same changes in the same order, so it goes through the same states as the leader, only later. Reads can go to any replica.

One leader and two followers A new order is written to the leader, whose replication log holds changes 1 to 9. The leader sends the log to two followers. Follower 1 has applied all nine changes; follower 2 has applied changes 1 to 7 and is behind. Product page reads go to the followers. Leader 1 2 3 4 5 6 7 8 9 ORDER Follower 1 1 2 3 4 5 6 7 8 9 READ Follower 2 1 2 3 4 5 6 7 READ LOG

Products also call these roles primary and secondary, primary and standby, or source and replica, and older documents say master and slave. A replica is any copy, the leader included.

In the store, a product page reads from a follower, and a new order goes to the leader. Since one node orders all writes, the replicas never disagree on which write came first.

Followers add read capacity, not write capacity. Every follower must apply every write, and that work takes capacity it could spend on reads. When the writes or the data outgrow one node, the data is split into shardsShardOne part of a dataset that is split across nodes; sharding is the split itself. Each row lives in exactly one shard, and each shard has its own leader and followers, so data and writes can grow past one machine.Read more: Sharding: Splitting a table across nodes, and each shard has its own leader and followers.

Every replica applies every write Two panels with a leader and two followers. Each bar is the full capacity of one node, split into writes and reads. With 10% writes, every node spends 10% on writes and 90% on reads, so the three nodes give 2.7 nodes of read capacity. With 50% writes, every node spends half on writes, so the three nodes give 1.5 nodes of read capacity. 10% writes 10% 90% leader 10% 90% follower 10% 90% follower READS ABOVE, WRITES BELOW Read capacity: 2.7 nodes 50% writes 50% 50% leader 50% 50% follower 50% 50% follower READS ABOVE, WRITES BELOW Read capacity: 1.5 nodes

The leader still has to decide when to tell the client that a write is done.

A shopper places an order. The leader saves it and answers “order placed”. A moment later the leader’s machine dies, before the change has left it. A follower takes over without the order, and the shopper’s confirmed purchase is gone.

With asynchronous replication, the leader confirms a write once it is durableDurabilityThe promise that a confirmed write survives a crash or a power loss. A write is durable only after a flush, such as fsync, puts it on disk. The store chooses whether to confirm a write before or after that flush.Read more: Storage engines: Surviving a crash on its own disk (more on durable writes), without waiting for followers. The change reaches them in the background. Writes are fast, and the leader keeps working even when every follower is slow or down. The cost is the case above: if the leader’s disk is lost, or a follower takes over before it got the write, a confirmed write disappears.

With synchronous replication, the leader confirms a write only after a follower has saved it to its own disk. A confirmed write then survives the loss of the leader, as long as the failover promotes that follower or another one that has every confirmed write. The cost is that every write waits for the follower, and when that follower is slow or down, writes stop. Making every follower synchronous is impractical: any one failed node would stop all writes.

Asynchronous and synchronous replication when the leader fails Two panels with the same steps in time. Asynchronous: the leader saves order 501 and answers OK at once, then fails before the follower gets the order, so the follower that takes over has no order 501. Synchronous: the leader saves the order, sends it to the follower, waits until the follower has saved it, and only then answers OK; when the leader fails, the follower that takes over has order 501. Asynchronous CLIENT LEADER FOLLOWER ORDER 501 saved OK FAILS no order 501 Confirmed order lost NEW LEADER Synchronous CLIENT LEADER FOLLOWER ORDER 501 saved ORDER 501 saved SAVED OK FAILS order 501 Confirmed order kept NEW LEADER

A common compromise is semi-synchronous replication: the leader waits for one follower and lets the others catch up in the background. While that follower answers, every confirmed write is on at least two nodes. If it fails, the system must pick another follower to wait for, or fall back to asynchronous replication. Some systems wait for a majority of replicas instead, such as three of five (more on majority commits).

A cloud region has several separate failure areas called availability zones, each with one or more data centers. Zones are close enough that waiting for a follower in another zone adds little, though every write still waits for that round tripRound tripThe time for a message to reach the other side and for the answer to come back. Also called RTT (round-trip time). Every handshake costs at least one round trip, so distance between client and server adds up fast.Read more: MDN: Round trip time. Spread replicas over zones to survive the loss of one zone, for example three replicas, one per zone. Surviving the loss of a whole region needs copies in another region.

Replicas spread over zones and regions The US region has three availability zones. The leader is in zone A. It waits for a synchronous follower in zone B before it confirms a write, and sends the change asynchronously to a follower in zone C and to a follower in the EU region. REGION US REGION EU ZONE A ZONE B ZONE C leader follower follower follower SYNC ASYNC ASYNC

Products differ in what “the follower has it” means: received, saved to disk, or applied and visible to reads. In PostgreSQL, once synchronous followers are configured, synchronous_commit chooses which of these to wait for, even per transactionTransactionA group of reads and writes that the database applies as one unit. Either all of its changes happen or none do, and concurrent transactions do not see each other's half-done work.Read more: Transactions: Changing several rows at once.

Do not assume that a semi-synchronous write is always on two nodes. MySQL waits for one follower for 10 seconds by default, then falls back to asynchronous replication without telling the client, until a follower catches up. Monitor this state.

Replicate asynchronously when losing the last moments of writes is acceptable. When a confirmed write must survive the loss of the leader, add one synchronous follower in another zone of the same region. Between regions, replicate asynchronously, unless losing a region must lose no confirmed write: then use a majority across regions and accept slower writes.

The store’s database needs an upgrade to a new major version, and sales must not stop for it. The analytics team also wants every change to orders as it happens. Both depend on what the replication log contains.

Statement-based replication sends each write statement, and every follower runs it again. The log is small, but every follower must get exactly the same result, and some statements do not give it: UUID() returns a new value on each node, and an UPDATE with LIMIT can pick different rows. Databases that use it mark such statements as unsafe and can log their changed rows instead.

Physical replication sends the WALWrite-ahead logA log on disk where changes are written before the data files they change. After a crash, the database replays it to rebuild the memtable of an LSM-tree or to repair half-written pages of a B-tree. A write is confirmed once its log record is flushed.Read more: Storage engines: Sorting writes in memory itself: which bytes changed in which page. A follower becomes a byte-for-byte copy of the leader. It is simple and exact, but it ties every replica to one storage format, usually one major version, so a major upgrade usually needs downtime. Every change to every indexIndexExtra data, built from the records, that finds a record without a full scan. An index speeds up reads, but it takes space and every write must update it too. So a database indexes only what its queries need.Read more: Storage engines: Finding a record fast page also crosses the network.

Logical replication sends changes to rows: the values of an inserted row, the key of a deleted row, the key and new values of an updated row. A commit recordCommit recordThe entry in the write-ahead log that marks a transaction as committed. The transaction is committed once this entry is on disk. After a crash, the database keeps transactions that have one and removes the rest.Read more: Transactions: Undoing a half-done transaction marks which changes commitCommitThe end of a transaction that makes all of its changes permanent at once. Other transactions see the changes only after the commit. A committed change is final: undoing it takes a new transaction that does the opposite.Read more: Transactions: Changing several rows at once together. The log does not depend on how each node stores its pages, so a follower can run a newer compatible version and builds its own indexes. Upgrade the followers first, check that one of them has everything it needs to take writes, then switch to it with a short pause. Some things may not be copied this way, such as schema changes in PostgreSQL. Other systems can read the same log, which is how CDCChange data captureCapturing the committed changes of a database, often from its log, and sending them to other systems. Derived stores follow the owner without dual writes from the app. Consumers must apply the changes of one record in order.Read more: Databases: Deciding who owns each fact feeds a warehouse or a search index with every change.

One update in three log formats The update sets the price of product 7 to 9.99. A statement log carries the SQL text, and the follower runs it again. A physical log carries the changed bytes of a table page and an index page, and the follower writes the same bytes. A logical log carries the changed row, product 7 with the new price, and a commit; the follower updates the row and its own indexes. WHAT THE LOG CARRIES WHAT THE FOLLOWER DOES Statement UPDATE products SET price = 9.99 WHERE id = 7 runs the SQL again Physical page 4211: bytes 96-103 changed index page 88: bytes 12-19 changed writes the same bytes Logical products id=7: price = 9.99 commit updates row 7 and its own indexes

In 2016, Uber moved from PostgreSQL to MySQL partly for this: physical replication sent every index change between data centers, and each major upgrade needed downtime.

Prefer a logical log when you need upgrades with only a short switch, replicas on different versions, or a stream of changes for other systems. A physical log fits a cluster of identical nodes. Avoid statement-based replication.

Before a sale, the store adds a follower to take more reads. Copying the leader’s files while writes go on gives a copy in which each file comes from a different moment.

Take a consistent snapshotSnapshotThe state of data at one moment. An in-memory store saves one to disk for recovery and replays the log written after it. A transaction can read from one, so data does not change under it.Read more: Storage engines: Back to memory of the leader while writes go on, and record the log position it matches. The new follower loads the snapshot and asks the leader for every change after that position. To catch up, it must apply changes faster than the leader produces new ones, so add followers before the load peaks. Once caught up, it receives changes as they happen. A follower that was down skips the snapshot: it asks for the changes after the last position it applied.

A new follower starts from a snapshot and catches up from the log The leader log holds changes 1 to 12. A snapshot matches log position 6. The new follower loads the snapshot, which covers changes 1 to 6, then applies changes 7 to 12 from the leader log. The leader must keep changes 7 to 12 until the follower has read them. Leader log 1 2 3 4 5 6 7 8 9 10 11 12 POSITION 6 KEPT FOR THE FOLLOWER New follower snapshot: 1-6 7 8 9 10 11 12 CATCH UP

Either way, the leader must still have the log from that position on. Keeping a long log takes disk space on the leader. If the needed part is deleted and no archive holds it, the follower starts again from a new snapshot.

A release with a bug sets the price of every product to zero. The leader applies the update, and within a second every follower applies it too. Replication copies a mistake as faithfully as a good write. A replica protects against losing a machine. Against a bad write or corrupted data, you need an older copy.

A backup is a copy of the data as it was at an earlier moment. Take a snapshot regularly and archive the database’s log since then, for example in object storageObject storageA store of files under keys in one flat namespace, read and written whole over HTTP. It is cheap, grows without limit, and keeps files durable. Amazon S3 is the best-known one.Read more: Databases: Storing files. With a snapshot and an unbroken log after it, the database can return to any moment that the log covers: restore the last snapshot before the mistake and replay the log up to just before it. This is point-in-time recovery. Changes after the end of the archived log are gone if the live copies are lost. A new follower can also start from these files, which takes the copying work off the leader.

A delayed follower applies every change an hour late. If a bad write is noticed within that hour, stop the delayed follower before it applies the write, and recover the data from it. It covers only that hour, so it does not replace backups.

Recovering from a bad write with a backup A time line from 13:00 to 14:40. At 14:02 a bad write sets every price to zero, and it is noticed at 14:40. The leader and the followers applied it at once and hold price 0. The delayed follower is one hour behind, at 13:40, so it still holds the good data. The backup is a snapshot from 13:00 plus the log archived since then. Recovery loads the snapshot and replays the archived log up to 14:01, just before the bad write. 13:00 13:20 13:40 14:00 14:20 14:40 BAD WRITE, 14:02 Leader, followers good data price 0 Delayed follower good data, 1 h late Snapshot 13:00 Archived log every change since 13:00 Recovery SNAPSHOT + LOG UP TO 14:01

In 2017, an engineer at GitLab deleted the leader’s data instead of the follower’s while rebuilding the follower. The regular backups had been failing without anyone noticing, and the team restored from a disk snapshot about six hours old, losing six hours of data.

Do not count replicas as backups. Keep backups in a separate place, restore from them regularly in a test, and measure how long a restore takes: a backup that was never restored may not work.

Product pages read from followers, and most of them are asynchronous. Now the store changes the price of a mug. For a moment the leader shows the new price and a follower the old one.

The delay between a write on the leader and the same write on a follower is replication lag. It is usually under a second, but it has no upper limit: an overloaded follower, a slow network, or a follower that is catching up can fall behind by seconds or minutes. A read from a follower that has not yet applied a write is a stale read.

Replication lag and a stale read Three rows show the price of a mug on each replica over time. All replicas start at 12.50. At 0 seconds the leader changes it to 9.99. Follower 1 applies the change at 0.2 seconds, follower 2 only at 3 seconds, so for 3 seconds follower 2 still shows the old price. A read from follower 2 at 1 second returns 12.50, a stale read. -1 s 0 s 1 s 2 s 3 s 4 s Leader 12.50 9.99 Follower 1 12.50 9.99 Follower 2 12.50 9.99 LAG: 3 s READ AT 1 s RETURNS 12.50

If the writes stop and every follower can still receive the remaining changes, the followers eventually apply all of them, and all replicas match. This promise is eventual consistency. It says nothing about how long that takes or what a read returns in the meantime. Asynchronous followers of a relational database are eventually consistent too.

Decide for each read whether a stale answer is acceptable. A product page can show a price that is a second old. The price charged at checkout and the stock check that allows a sale belong on the leader, inside the transaction that writes the order.

Watch the lag. The leader’s log position minus the position a follower has applied shows how much log the follower still has to apply. To see the lag in seconds, let the leader write the current time into a row every second: the lag is the time now minus the time in the follower’s copy of that row, give or take that second. A follower that falls too far behind should stop serving reads until it catches up.

“Consistency” here means whether replicas show the same data. In transactions, the same word means that data follows its rules (more on consistency in transactions).

Lag also breaks what a user expects from their own actions.

A shopper changes the delivery address. The write goes to the leader, and the checkout page right after it reads from a follower that has not applied it yet. The page shows the old address, and the shopper thinks the change was lost.

A user does not see their own write The shopper sends a new address to the leader, which stores it at log position 41. The checkout page then reads from a follower that has applied the log only up to position 39, so it returns the old address. SHOPPER LEADER FOLLOWER NEW ADDRESS pos 41 pos 39 READ ADDRESS OLD ADDRESS

Read-your-writes means that a user’s reads include the user’s own completed writes. It promises nothing about other users’ writes, which may still show up late. Ways to provide it:

  • Read the data that a user can change from the leader, and everything else from followers. The shopper’s own addresses and cart come from the leader; product pages come from followers. This works when the data a user can change is small and easy to tell apart.
  • For a short time after a user’s write succeeds, such as a minute, send that user’s reads to the leader. Take out of reads any follower that is further behind than that. Store the time of the user’s last write with their session.
  • Remember the leader’s log position after the user’s write, or a token the database returns for it. A follower serves that user only after it has applied the log up to that point; otherwise the read waits or goes to another replica. This needs a database that exposes such tokens. MongoDB does it in causally consistent sessions with majority reads and writes.
Three ways to provide read-your-writes Three panels with the same layout. Own data from the leader: a read of the shopper's own address goes to the leader, a product page goes to a follower. Leader for a minute: a read 10 seconds after the shopper's write goes to the leader, a read 2 minutes after goes to a follower whose lag is under a minute. Read after a position: the read carries log position 41; a follower at 39 waits, a follower at 41 answers. Own data from leader shopper leader MY ADDRESS follower PRODUCT PAGE Leader for a minute shopper leader 10 S AFTER MY WRITE follower 2 MIN AFTER lag under 1 min Read after position shopper follower, 39 WAITS OR SKIPPED follower, 41 AFTER 41: ANSWERS

To keep the guarantee across a user’s phone and laptop, store the time or token of their last write on the server, so both devices use it.

When followers are in other regions and the leader is far away, every read sent to the leader pays the trip. Facebook reduced such stale reads in 2013 with per-key markers: after a user in a region without the leader changed some data, a cacheCacheA copy of data or results that is faster to read than the original and can be rebuilt from it. It cuts latency and load on the source while it answers most reads. Losing it makes a system slower but not wrong.Read more: Caching: When a cache pays off miss on that data read it from the leader’s region while the local copy might still be behind.

Send a user’s reads to the leader for a short time after their write, or read with a log position when the database supports it. Keep the leader for the reads that need it, so followers still carry most of the load.

A shopper opens the order history and sees “shipped”. They reload the page, the request lands on a follower that is further behind, and the order is back to “paid”. Each answer is a correct past state, but the second is older than the first.

Monotonic reads means that after a user has seen some data, they never see an older version of it. It is weaker than always reading the latest data and stronger than eventual consistency.

The simple fix is to send each user to the same replica, for example by a hash of the user ID instead of a random choice. This works while that replica is up. When it fails and the user moves to another replica, they can step back. To avoid that, remember the highest log position the user has seen, and read only from replicas that have reached it.

Two reads from followers at different points Two panels. Left: the first read goes to follower A at position 50 and shows "shipped"; the reload goes to follower B at position 45 and shows "paid", an older state. Right: both reads go to follower A, chosen by a hash of the user ID, and show "shipped". Random follower SHOPPER FOLLOWER A FOLLOWER B pos 50 pos 45 READ SHIPPED RELOAD PAID Same follower SHOPPER FOLLOWER A FOLLOWER B pos 50 pos 45 READ SHIPPED RELOAD SHIPPED

A shopper asks a question on a product page, and the seller answers it. Another shopper sees the answer before the question.

One query to one replica that follows one log cannot show this: the replica applies the writes in the leader’s order, so a query sees the history up to some point. It happens when one page combines reads taken at different points. Parts of the data can replicate separately, such as questions and answers in different shards with their own logs. Or two queries of one page can run at different moments, on different followers or even on the same one.

Consistent prefix reads means that when writes happen in some order, every reader sees them in that order and never sees a later write without the earlier ones.

Keep writes that depend on each other in one log, and read them together: store the questions and answers of a product in the same shard, keyed by the product, and load them in one query, or in one transaction that reads a consistent picture. When that is impossible, the system has to track which write depends on which.

Two related guarantees cover a user’s own writes. With monotonic writes, a user’s writes apply in the order the user made them. With writes follow reads, a write applies after the writes the user had read before making it, so a reply never comes before its question. One leader whose log survives gives both to a user who waits for each write to finish before the next. Failovers that lose writes, parallel requests, and several leaders need explicit tracking.

The leader’s machine dies during a sale. Followers still serve product pages, but nobody accepts orders.

Failover makes a follower the new leader:

  1. Suspect the failure. Nodes check each other, and a leader that does not answer within a timeout, such as 30 seconds, is treated as dead, though it may only be slow.
  2. Pick the new leader: a follower that has every confirmed write, which is the synchronous follower when there is one, and among those the most up-to-date. The other replicas elect it by majority, or a separate controller appoints it.
  3. Stop the old leader from writing, send writes to the new one, and make the other followers replicate from it. When the old leader comes back, it must become a follower.
Failover to the most up-to-date follower Two panels with the same three nodes and log positions 94 to 100. Before: the leader has changes up to 100 and fails; follower 1 has changes up to 98, follower 2 up to 95. Changes 99 and 100 were confirmed but exist only on the leader. After failover: the old leader is down, follower 1 is the new leader with changes up to 98, and follower 2 replicates from it and catches up to 98. Changes 99 and 100 are lost. Before: the leader fails Leader fails 94 95 96 97 98 99 100 Follower 1 94 95 96 97 98 Follower 2 94 95 After failover Old leader down down New leader was follower 1 94 95 96 97 98 Follower 2 follows new leader 94 95 96 97 98 LOST 99 and 100 confirmed, only on the leader 99 and 100 are gone

A planned switch, for maintenance or an upgrade, loses nothing: stop writes on the old leader, let a follower apply the whole log, then promote it. An unplanned failover has no such pause. A team can run it by hand or let a tool do it, and each step can go wrong:

  • With asynchronous replication, the new leader lacks the last writes of the old one. When the old leader returns, its extra writes conflict with new ones and are usually thrown away, so confirmed orders disappear.
  • Other systems may have seen the lost writes. In 2012, an out-of-date MySQL follower became GitHub’s leader. Records in RedisRedisAn in-memory server that stores data structures under keys and runs commands on them. A small command takes far less time than the network round trip, so it often sits next to a database for caches, sessions, counters, and queues. Its copies are asynchronous, so a crash or failover can lose recent writes.Read more: Redis: Taking hot data off the database that pointed to MySQL rows now pointed to the wrong data, and 16 private repositories were visible to outsiders for seven minutes.
  • The old leader may only be slow or cut off from the others, not dead. It keeps accepting writes while the new leader does too. Two leaders at once is split brain, and their writes conflict.
  • A short timeout fails over on a load spike or a network hiccup, and the failover adds load at the worst time. A long timeout keeps the store without writes for longer.

The defense against split brain is fencing: before the new leader takes writes, make sure the old leader’s writes cannot take effect. Each new leader gets a higher number, and every node and storage system rejects writes that carry an older number. Some setups also cut the old leader off from the storage or the network. A timeout alone does not prove that the old leader has stopped: a paused process can wake up and keep writing (more on fencing tokens).

An old leader that wakes up, without and with fencing Two panels with the same events. The old leader pauses; meanwhile a new leader is elected and writes to the storage. Then the old leader wakes up and writes too. Without fencing, the storage accepts both writes, so two leaders write at once. With fencing, each leader has a number, 1 for the old one and 2 for the new one; the storage accepts the write with 2, then rejects the write with 1 because 1 is lower than 2. Without fencing OLD LEADER STORAGE NEW LEADER paused elected WRITE accepted WAKES UP WRITE accepted too Two leaders write at once With fencing OLD LEADER STORAGE NEW LEADER number 1 number 2 paused elected WRITE, 2 accepted, now 2 WAKES UP WRITE, 1 rejected: 1 < 2 The old leader is fenced off

Set two recovery targets: the RPO (recovery point objective), the most recent data you may lose, and the RTO (recovery time objective), the longest the recovery may take. Then check that the design meets them for the failures you plan for. A failover to an asynchronous follower typically loses up to its lag and takes seconds to minutes. A restore of a large database from backups can take hours.

Promote a follower that has every confirmed write, and keep a synchronous follower when losing confirmed writes is not acceptable. Fence the old leader before the new one takes writes. Pick a timeout that rides out normal spikes, and practice failover before you need it. Fail over automatically within a region; across regions, where a false failover costs the most, let a person decide. After a failover, add a new follower soon: until then, one more failure can lose data.

The store opens in Europe, and the leader is in the US. Followers in Europe serve product pages quickly, but every cart change and order crosses the ocean. If the US region goes down, European shoppers cannot buy anything.

Between regions there are four designs:

  • One leader region. All writes go to one region, and the other regions hold followers. Rules over all the data, such as “do not sell more than the stock”, stay simple, since one node sees every write. Writes from far away are slow, and losing the leader’s region means a failover across regions with asynchronous lag. In 2018, a 43-second network break made GitHub’s tool promote new leaders on the other coast. Both coasts then held writes the other lacked, the site was degraded for 24 hours, and GitHub stopped promoting leaders across regions.
  • A home region per user. Assign each user’s records to one region, usually the nearest, and route their writes there. Writes stay local while the user is near home. Every record still has one leader, but different records have leaders in different regions. A user who travels writes across the ocean, and moving a user’s home is a special operation.
  • A majority across three regions. Place the voting replicas so that any two regions hold a majority, and confirm a write once that majority has saved it. Losing a whole region then loses no confirmed write, and the other two keep working. Every write pays a round trip to another region. Databases built on consensusConsensusSeveral nodes agreeing on one value or one order of writes, even when some fail. Crash-tolerant majority protocols need 2F + 1 voting nodes to keep working after F crashes, and progress needs timely messages. Raft, Paxos, and ZAB are common algorithms.Read more: Consistency: Agreeing on one value work this way. DynamoDB’s strong mode uses exactly three regions: three replicas, or two replicas and a witness that only helps form the majority (more on majority commits).
  • Multi-leader replication gives each region a leader that accepts writes and replicates them asynchronously to the leaders of the other regions. Writes are local, and a region keeps working when it is cut off. The cost is that two regions can change the same record at once. No node sees every write, so independent writes cannot check a rule over all the data: two regions can sell the last mug, or register the same email. Such rules need coordination, usually one leader. Many databases add multi-leader on top of a single-leader design, and features built for one writer can fail: generated IDs can collide, and a constraintConstraintA rule that the database checks on every write, such as a unique key or a foreign key. The database rejects a write that breaks the rule, so a bug in one service cannot break it.Read more: Databases: Storing each fact once checked in one region does not see the rows of another.
Four designs for writes from several regions Four panels with the same three regions, US East, US West, and EU. One leader region: the leader is in US East and the other regions hold followers. A home region per user: US users have their leader in US East, EU users in the EU. A majority across three regions: a write waits until two of the three regions have saved it. Multi-leader: each region has a leader, and the leaders replicate to each other asynchronously. One leader region US EAST leader US WEST follower EU follower ASYNC EU writes cross the ocean Home region per user US EAST leader, US US WEST follower EU leader, EU ASYNC BOTH WAYS Each record has one leader Majority across regions US EAST leader US WEST follower EU follower SYNC, MAJORITY Write waits for 2 of 3 regions Multi-leader US EAST leader US WEST leader EU leader ASYNC BOTH WAYS Local writes, conflicts

A mobile app that works offline is the extreme case of multi-leader. Each device keeps a local copy and accepts writes, and its lag can be hours or days, so conflicting changes are normal and must merge on their own.

Start with one leader region and followers in the others. Give users a home region when their writes must be fast everywhere. Use a majority across three regions when losing a region must lose no confirmed write, and multi-leader only for data whose concurrent changes can be merged.

Several leaders can change the same record before either sees the other’s change.

Two employees edit the title of the same product, one in Europe and one in the US. Each leader accepts its change, and when the changes replicate, each leader receives a different title for the same record.

Two leaders change the same title at once Both leaders start with the title "Red mug". An employee in the EU changes it to "Mug, red" on the EU leader, and an employee in the US changes it to "Red coffee mug" on the US leader. Each leader sends its change to the other, so each ends up with two different titles for the same record: a conflict. EU LEADER US LEADER Red mug Red mug EMPLOYEE EMPLOYEE Mug, red Red coffee mug REPLICATE Mug, red Red coffee mug Red coffee mug Mug, red SAME RECORD, TWO TITLES

Concurrent writes are writes where neither knew about the other. Time does not matter: two edits made hours apart on two offline devices are concurrent too. A write made after reading the other one is not concurrent: it builds on it and replaces it.

A timestamp cannot tell these two cases apart. A version vector can. Each version of a key carries one counter per replica, and a replica increases its own counter on each write it accepts. A client sends back the vector it read together with its write. With two replicas, EU and US, the vector [EU 2, US 1] comes after [EU 1, US 1]: it is ahead in one counter and behind in none, so its write saw the other. The vectors [EU 2, US 1] and [EU 1, US 2] are each ahead in one counter, so their writes are concurrent. Each vector adds a counter for every replica that writes the key.

Version vectors tell a replacement from a concurrent write Two panels. Both replicas start with "Red mug" and the vector EU 1, US 1. Left: the EU replica accepts "Mug, red" and raises its own counter to EU 2, US 1. When this reaches the US replica, the new vector is ahead in one counter and behind in none, so "Mug, red" replaces "Red mug". Right: at the same time the US replica accepts "Red coffee mug" with EU 1, US 2. When the writes cross, each vector is ahead in one counter, so the writes are concurrent and both replicas keep both titles. One write replaces another EU REPLICA US REPLICA Red mug [EU 1, US 1] Red mug [EU 1, US 1] Mug, red [EU 2, US 1] Mug, red [EU 2, US 1] [EU 2, US 1] VS [EU 1, US 1] Ahead, not behind: replaces REPLICATE Concurrent writes EU REPLICA US REPLICA Red mug [EU 1, US 1] Red mug [EU 1, US 1] Mug, red [EU 2, US 1] Red coffee mug [EU 1, US 2] keeps both keeps both [EU 2, US 1] VS [EU 1, US 2] Each ahead in one: concurrent REPLICATE

Ways to handle concurrent writes, from the simplest:

  • Avoid them: send all writes for a record to one leader, as with a home region. Conflicts come back when the leader of a record changes.
  • Last write wins: keep the write with the latest timestamp and drop the other. All replicas end up equal, but a successful write is silently lost, and with clocks that differ a later write can lose to an earlier one. DynamoDB global tables resolve conflicts this way by default.
  • Keep both and merge: the database stores both versions, called siblings, and the app merges them on the next read or asks the user. In 2007, Amazon’s cart merged siblings by keeping every item from every version, so an added item was never lost, but a removed item could come back.
  • Use data types that merge themselves. A CRDT is a counter, set, list, or text that merges concurrent changes by a fixed rule, so every copy reaches the same value without asking the app. Amazon’s cart brought removed items back because a plain union cannot tell “removed on one device” from “still there on the other”. A suitable CRDT set tells an old copy of an item from a new addition, so merging an old copy does not undo a removal. Each set type also decides what wins when an add and a remove are concurrent.
Four ways to handle the same two cart changes Four panels with the same history. The cart holds Book and DVD. Device 1 removes Book, device 2 adds Soap. One leader: both writes go to one leader, which applies them in order, giving DVD, Soap with no conflict. Last write wins: the two versions DVD and Book, DVD, Soap conflict, the later one by the clock, DVD, is kept, and Soap is lost. Siblings: the app merges both versions as a union and gets Book, DVD, Soap, so the removed Book comes back. CRDT set: the set knows Book was removed and gives DVD, Soap. One leader Book, DVD DEVICE 1: REMOVE BOOK DVD THEN DEVICE 2: ADD SOAP IN ORDER DVD, Soap No conflict Last write wins Book, DVD DEVICE 1: REMOVE BOOK DEVICE 2: ADD SOAP DVD Book, DVD, Soap LATER WINS DVD Soap is lost Siblings Book, DVD DEVICE 1: REMOVE BOOK DEVICE 2: ADD SOAP DVD Book, DVD, Soap APP: UNION Book, DVD, Soap Book comes back CRDT set Book, DVD DEVICE 1: REMOVE BOOK DEVICE 2: ADD SOAP DVD Book, DVD, Soap SET RULE DVD, Soap Book stays removed

Some rules cannot be merged. Two regions that each sell the last mug have both done something correct on their own, and no merge makes the stock right (more on rules across rows). Such writes need coordination, usually one leader.

Avoid conflicts by giving each record one leader. Where they cannot be avoided, merge them with siblings or CRDTs.

Do not use last write wins where losing one of two concurrent writes matters, such as a cart or a counter. It drops writes without an error. It is safe when each key is written once, or when the lost write does not matter.

Adding an item to the cart must work even while nodes fail. A failover that stops writes for 30 seconds loses sales. Amazon described Dynamo, its store for this case, in a 2007 paper, and Cassandra and similar stores follow its design.

Leaderless replication lets any replica accept writes. The client, or a node acting for it, sends each write to all N replicas of the key and treats it as done once W of them confirm. A read asks the replicas, waits for R answers, and compares their versions. It returns the winner under the store’s conflict rule, or all concurrent versions for the app to merge. A replica that was down simply misses the writes made meanwhile.

N is the number of replicas of a key. W is how many must confirm a write, and R is how many must answer a read: these are the quorum sizes. If W + R > N and both use the same N replicas, any W replicas and any R replicas share at least one. With N = 3, any two replicas share one with any other two. After a confirmed write, a read then reaches at least one replica that has it, as long as the replicas keep what they confirmed.

A quorum write and a quorum read share a replica Two panels with three replicas, A, B, and C, so N is 3. Write with W = 2: the client sends version 2 to all three; A and B save it and confirm, C is down and keeps version 1; two confirmations make the write done. Read with R = 2: later, C is back; the client gets answers from B and C, version 2 and version 1, and returns version 2, the newer one. Replica B is in both sets. Write, W = 2 client REPLICA A REPLICA B REPLICA C V2 V2 v2 v2 down: v1 Done: 2 of 3 saved v2 Read, R = 2 client REPLICA A REPLICA B REPLICA C V2 V1 slow v2 v1 Returns v2, the newer one

The common setting is N = 3, W = 2, R = 2: reads and writes keep working with one replica down. With N = 5, W = 3, R = 3, they survive two. For majority reads and writes, three or five replicas are common: a fourth replica raises the majority to three but still survives only one failure. Shifting the numbers moves the cost: W = N and R = 1 makes reads cheap, but one failed replica stops writes.

The overlap makes a read return the latest value in normal work, but it is not a guarantee:

  • With last write wins by the clock, a node with a fast clock can make an older write beat a newer one, and the newer write is lost.
  • While a write has reached fewer than W replicas, it is not confirmed yet. A read in this window can see the new value, and a later read can still see the old one.
  • A write that reached fewer than W replicas reports a failure, but it is not undone where it landed, and later reads may or may not see it. A client that got an error or a timeout cannot assume the write did not happen, so it must retry safely (more on failed calls).
  • The replicas of a key can change, for example while nodes are added, or a replica can be restored from an old copy. A write and a later read may then miss each other.
  • A sloppy quorum accepts confirmations from substitute nodes when the usual replicas do not answer. Writes keep working, but a read from the usual replicas can miss the write until the substitutes hand it back.
  • Two concurrent writes are both “the latest”, which is a conflict to resolve.
Six ways a quorum read can miss the latest write Six panels with replicas A, B, and C, N = 3, W = 2, R = 2. Clocks differ: v1 is stamped 10:05 by a fast clock, the later v2 is stamped 10:02, and last write wins keeps v1. Read during a write: v2 has reached only A; a read of A and B gets v2, a later read of B and C gets v1. Failed write: only A saved v2 and the client got an error, but a read of A and B can still return v2. Replica restored: v2 was confirmed by A and B, then B was restored from an old copy, and a read of B and C gets v1. Sloppy quorum: A and B were cut off, C and a substitute node D confirmed v2, and a read of A and B gets v1. Concurrent writes: write a reached A and B, write b reached B and C, and a read gets both. Clocks differ v1, FAST CLOCK: 10:05 A v1 B v1 C v1 THEN v2, CLOCK: 10:02 LAST WRITE WINS KEEPS v1 The newer v2 is lost Read during a write v2 HAS REACHED A ONLY A v2 B v1 C v1 READ A, B GETS v2 LATER READ B, C GETS v1 A later read goes back Failed write ONLY A SAVES v2 A v2 B v1 C v1 THE CLIENT GETS AN ERROR READ A, B CAN GET v2 A failed write shows up Replica restored A, B CONFIRM v2 A v2 B v1 C v1 B RESTORED FROM AN OLD COPY READ B, C GETS v1 A confirmed write is missed Sloppy quorum A, B CUT OFF; D STANDS IN A v1 B v1 C v2 D v2 C AND D CONFIRM v2 READ A, B GETS v1 The read misses the write Concurrent writes WRITES a AND b AT ONCE A a B a, b C b a ON A, B; b ON B, C READ A, B GETS a AND b A conflict to resolve

In a 2013 Jepsen test, Cassandra with quorum reads and writes kept updating one cell, and last write wins silently dropped 28% of the confirmed writes. Treat W and R as a dial for how often a read is stale. Lowering W makes writes wait for fewer replicas, and lowering R does the same for reads, so each survives more failed replicas. With W + R ≤ N, though, the read and write sets may not overlap. How often a read is then stale depends on how fast changes spread: a 2012 study based on production latencies found such reads often fresh within tens of milliseconds. Quorums alone do not make reads linearizableLinearizabilityThe guarantee that all reads and writes fit one order on a single copy, matching real time. Once a write finishes, every later read returns it or a newer value. It needs coordination between copies, so it costs latency and makes some nodes refuse requests during a network partition.Read more: Consistency: Acting as one copy.

Without a leader there is no failover: a slow or dead replica is one of N, and the client uses the answers that come first. The costs are conflicts on concurrent writes and lag that is hard to measure without one log. A larger quorum also waits for more replicas, so a slow one delays it more often.

Across regions, a write can wait only for a quorum in its own region and reach the other regions in the background, as Cassandra’s LOCAL_QUORUM does. Writes stay fast, but a read in another region may be stale.

Dynamo, the 2007 design, is leaderless. DynamoDB, the AWS service, is not: each shard has a leader elected among its replicas.

A replica that missed a write keeps the old value until something repairs it.

A replica was down for an hour during the sale. It is back, and many of its carts are old.

Read repair fixes replicas during reads: when a read finds a replica that lacks the newest version, or lacks concurrent versions that the others keep, it writes them back to it. It fixes keys that are read often and leaves rarely read keys old.

Read repair A reader asks replicas A, B, and C for cart 7. A and B answer with version 2, C answers with version 1, so C missed a write. The reader returns version 2 and writes version 2 back to C. READER REPLICA A REPLICA B REPLICA C READ CART 7 FROM A, B, C v2 v2 v1 ANSWERS: v2, v2, v1 returns v2 C is behind WRITE BACK v2 TO C v2

Hinted handoff covers the outage itself: while a replica is down, another node stores the writes meant for it with a hint and passes them on when it returns. This is how the writes of a sloppy quorum get home. Cassandra’s ANY level can confirm a write from a stored hint alone, before any usual replica can serve the new value.

Hinted handoff A writer sends version 2 to replicas A, B, and C. C is down, so node D stores the write with a hint that it belongs to C. A and B save it. When C comes back, still with version 1, D hands version 2 over to C. WRITER REPLICA A REPLICA B REPLICA C NODE D down WRITE v2 v2 v2 hint for C v2 back: v1 HAND OFF v2 v2

Anti-entropy compares replicas in the background and copies what each one is missing, so it also repairs keys that nobody reads. Sending all the data to compare it would cost too much, so replicas compare a Merkle tree: hashes of key ranges, where each upper hash covers the hashes below it. Replicas compare the top hashes, go down only where they differ, and copy just those ranges. Repair still costs disk and network time.

Anti-entropy with Merkle trees Replicas B and C each build a tree of hashes over keys 1 to 100. The top hashes differ. One level down, the hashes of keys 1 to 50 match, so that half is skipped; the hashes of keys 51 to 100 differ. One more level down, keys 51 to 75 match and keys 76 to 100 differ, so only keys 76 to 100 are copied. Replica B keys 1-100 7e2 1-50 a3f 51-100 c18 1-25 5d0 26-50 61b 51-75 2c4 76-100 9aa Replica C keys 1-100 4b9 1-50 a3f 51-100 d02 1-25 5d0 26-50 61b 51-75 2c4 76-100 e5f 1. TOP HASHES DIFFER 2. 1-50 MATCH: SKIP; 51-100 DIFFER: GO DOWN 3. COPY KEYS 76-100 ONLY

A delete needs the same care. It is written as a tombstoneTombstoneA record that marks a key as deleted. Files that are only appended to cannot erase a record, so a delete writes a tombstone. It must stay until no older file can bring the deleted value back, and until compaction removes the key, the deleted data is still on disk.Read more: Storage engines: Reclaiming space, and after a grace period the tombstone may be purged. If a replica missed the delete and the tombstone is purged before repair reaches that replica, the deleted value comes back from it. A replica that was away longer than the grace period must be rebuilt from a current copy. Cassandra’s default grace period is 10 days, and its documentation asks to repair every node at least every 7 days.

A deleted cart comes back after the tombstones are purged Three rows show replicas A, B, and C over time. On day 0 cart 42 is deleted: A and B replace it with a tombstone, but C is down and misses the delete, so it keeps cart 42. After the 10-day grace period, A and B purge the tombstone and hold nothing. On day 11 repair compares the replicas, finds cart 42 only on C, and copies it back to A and B, so the deleted cart returns. DAY 0 DELETE DAY 10 PURGE DAY 11 REPAIR Replica A cart 42 tombstone nothing cart 42 again Replica B cart 42 tombstone nothing cart 42 again Replica C cart 42 down cart 42: missed the delete GRACE PERIOD: 10 DAYS REPAIR COPIES IT FROM C The deleted cart is back on every replica

Finish repair on every replica within the tombstone grace period, and rebuild replicas that were away longer. Rely on read repair only for keys that are read often, and on hinted handoff only for short outages.

In the store, orders and stock use one leader. Accounts use a home region per user, which still gives each record one leader. Carts use leaderless replication. Each scheme answers “who accepts writes” differently, and the rest follows:

Single leader Multi-leader Leaderless
Who accepts writes one leader a leader per region or device any replica
A write is done on the leader, plus synchronous followers on the local leader on W replicas
Concurrent writes ordered by the leader conflicts to resolve conflicts to resolve
Global rules (stock, unique names) checked in local transactions need extra coordination need extra coordination
When the leader or a node fails leader: failover, writes pause other leaders keep writing nothing, while W and R replicas still answer
Fits most systems writes in several regions, offline apps writes that must keep working while nodes fail, and can merge

Most designs need a single leader: followers in other zones and regions, asynchronous or semi-synchronous replication, reads from followers where a stale answer is fine, and read-your-writes where users expect to see their changes.

During a network break, a system either refuses some requests or lets copies disagree. A single leader refuses writes on the side without it; multi-leader and a sloppy quorum keep writing and let copies disagree. This choice is what CAPConsistency, availability, partition toleranceThe rule that during a network break a replicated system either refuses some requests or lets copies disagree. It applies only while the network is split. In normal work the trade-off is between latency and consistency.Read more: Consistency: Answering during a network partition describes.

When asked how data is replicated, answer three questions: who accepts writes, when a write counts as done, and what a read may see. Then say what a failover loses, and which reads must go to the leader.

  • Replication keeps the same data on several nodes. In the usual design, one leader takes all writes and sends them to followers in one order through the replication log; reads can go to any replica.
  • Asynchronous replication is fast but can lose confirmed writes that had not reached a follower. Synchronous replication waits for a follower to save the write, keeps it if that follower is promoted, and stops writes when the follower fails. Use one synchronous follower in another zone for writes that must survive the loss of the leader.
  • Statement logs need statements that give the same result everywhere. Physical logs tie all replicas to one storage format, usually one version. Logical row logs allow upgrades with a short switch between compatible versions and feed CDC.
  • A new follower loads a snapshot with its log position, then catches up from the log, applying changes faster than they arrive. The leader must keep the log long enough for followers that are behind.
  • Replicas copy bad writes too. Keep separate backups with an unbroken archived log for point-in-time recovery, and test restores.
  • Followers add read capacity, not write capacity, and they lag, usually under a second but without limit. Eventual consistency promises only that copies match once writes stop and changes can arrive. Read from the leader whatever decides a write.
  • Read-your-writes: read a user’s own data from the leader, or from the leader for a short time after their write, with followers further behind than that taken out of reads, or from a follower that has reached the position of their write.
  • Monotonic reads: keep each user on one replica while it is up, and track the newest position they have seen to stay safe after a switch.
  • Consistent prefix: keep writes that depend on each other in one log, such as one shard, and read them in one query or one consistent snapshot. One leader with an intact log also applies a user’s writes in order and after what the user read.
  • A planned switch loses nothing. An unplanned failover suspects a dead leader after a timeout, promotes a follower that has every confirmed write, fences the old leader, and redirects writes. It can lose asynchronous writes, confuse systems that saw them, and cause split brain without fencing. RPO is the data-loss target, RTO the recovery-time target.
  • Across regions, use one leader region by default, a home region per user for fast local writes, a majority across three regions when a region loss must lose nothing, and multi-leader only for data that can merge. Global rules need coordination, usually one leader.
  • Concurrent writes conflict. Avoid conflicts with one leader per record; last write wins drops writes; siblings and CRDTs merge them. Version vectors tell concurrent writes from replacements.
  • Leaderless replication writes to W and reads from R of N replicas, usually 3, 2, and 2. With W + R > N a read usually sees the latest confirmed write, but clocks, concurrent writes, failed writes, changed or restored replicas, and sloppy quorums break it.
  • Replicas that missed writes are fixed by read repair, hinted handoff, and anti-entropy. Finish repair within the tombstone grace period, or deleted data comes back, and rebuild replicas that were away longer.
  • Default to a single leader. Pick multi-leader for writes in several regions or offline, and leaderless for writes that must keep working while nodes fail and can merge.

© 2026 Grig Alex · CC BY-SA 4.0