Guide

In single-leader replication, one node, called the leader (or primary), accepts every write. It records each change in its log and streams that log to followers (replicas or standbys). Followers apply the changes in the same order. Reads can go to the leader for fresh data, or to followers to spread the load.

Postgres, MySQL, SQL Server, MongoDB replica sets, Redis and most managed databases use this model. Its main strength is one total order of writes. Only one node decides the order, so there are no write conflicts.

The big idea

Think of a newspaper. One editor decides what goes into each edition, and every printing plant receives the same edition. A plant far away gets it later. The key decision is when the editor says "this edition is final". For a database, that moment is when the leader tells the client "committed".

  • Asynchronous. The leader confirms as soon as it has written the change locally. This is fast and unaffected by slow followers. But a write confirmed just before a leader crash may never have left the leader.
  • Synchronous. The leader waits until at least one follower has durably received the change. No confirmed write is lost if the leader dies. Every commit waits a round trip, and writes stall if the synchronous follower is down.
  • Semi-synchronous. The leader waits for some followers, not all. MySQL's semi-sync plugin waits for one replica to receive the event. In Postgres, synchronous_standby_names = 'ANY 1 (replica_a, replica_b)' waits for any one of two standbys.

Walking through it

A trace shows the difference. The leader commits transaction 881 and then crashes:

Asynchronous:
  leader writes 881, confirms "committed" to client, crashes before sending 881
  follower never receives 881
  new leader is promoted without 881 → the client was told success, and the order is gone

Synchronous (one follower must confirm first):
  leader writes 881, waits for the follower to confirm
  client sees "committed" only after the follower has the change
  leader crashes → the follower has 881 → nothing confirmed is lost

The cost depends on distance. Inside a region, a cross-zone round trip is about 1 to 2 ms, so synchronous replication there adds little. Across continents the round trip is 70 to 150 ms, so cross-region replication is almost always asynchronous.

Postgres has a stronger option, synchronous_commit = remote_apply. The commit waits until the standby has applied the change. That means a read on that standby right after the commit sees the new data. Amazon Aurora uses a quorum: each write goes to 6 storage copies across 3 zones, and it is confirmed once 4 of them have it. It survives losing a whole zone plus one more copy.

Common mistakes

  • Using asynchronous commits for writes you cannot lose. Use synchronous or semi-synchronous replication for those.
  • Assuming synchronous replication is free. The commit waits, and writes stall if the synchronous follower is down.
  • Forgetting that a follower can fall behind. Its lag decides how much data a failover can lose.

Check yourself

  1. Why does one leader avoid write conflicts?

    Answer: only one node orders writes, so there is one history for every follower to follow.

  2. What does remote_apply buy you over a plain synchronous commit?

    Answer: a read on that standby right after the commit sees the change, because the commit waited for the standby to apply it.

Key takeaways

  • The leader accepts all writes and streams its log to followers, which apply them in order.
  • Asynchronous commits are fast but can lose confirmed writes on a crash.
  • Synchronous commits protect confirmed writes, at the cost of a round trip per commit.
  • Semi-synchronous replication waits for some followers, which is a common middle ground.

Further learning

Next, see what the leader actually sends to followers, in how changes are shipped.