Replication means keeping copies of the same data on multiple nodes. You do it for three reasons: spread read load across replicas, survive node failures without downtime, and recover from disasters by having data in a separate region. The hard part isn’t copying a static snapshot. It’s propagating every subsequent write to all replicas correctly while nodes crash, networks partition, and clients read concurrently. Get it wrong and you get stale reads, lost writes, or two nodes that both think they’re the primary.

Replication Models

Single-Leader (Leader-Follower)

All writes go to one designated node (the leader). Replicas receive a stream of changes and apply them in order, serving read traffic. This is the default model for PostgreSQL (streaming replication), MySQL (binlog replication), SQL Server Always On Availability Groups (AG replicates transaction log records to secondaries; WSFC provides health detection, quorum, and failover orchestration), and MongoDB replica sets.

Reads can be routed to replicas using ApplicationIntent=ReadOnly in SQL Server connection strings, or by pointing read-heavy workloads at a replica endpoint. Write throughput is bounded by the single leader; you can’t scale writes by adding replicas.

Failover requires electing a new leader. SQL Server uses Windows Server Failover Clustering (WSFC) quorum; PostgreSQL commonly relies on an external manager such as Patroni or repmgr. A voting quorum prevents two candidates in the same voting configuration from both winning a majority. It does not stop an isolated former leader from continuing to serve stale endpoints or accept writes; leases, endpoint ownership, and fencing must revoke that old leader before the replacement is writable.

Multi-Leader

Multiple nodes accept writes independently and propagate changes to each other. Used by CouchDB, and SQL Server’s peer-to-peer transactional replication. The benefit is write availability across datacenters: a write to the EU node doesn’t wait for the US node to acknowledge.

The cost is mandatory conflict resolution. If two leaders accept a write to the same row concurrently, you have a conflict. Common strategies:

  • Last-write-wins (LWW): keep the write with the higher timestamp. Simple, but lossy. The other write is silently discarded. Dangerous for financial or inventory data.
  • CRDTs: data structures designed to merge automatically (counters, sets). Only works for specific data shapes.
  • Application-level resolution: the application receives both versions and decides. Most flexible, most work.

In SQL Server peer-to-peer transactional replication, the recommended practice is write partitioning (each node owns a non-overlapping subset of rows). Peer-to-peer replication offers conflict detection but is not a general-purpose conflict resolution platform like CRDTs; avoiding same-row writes across nodes is the primary design constraint.

Leaderless (Dynamo-style)

In Dynamo-style systems such as the original Amazon Dynamo, Cassandra, and Riak, several replicas can accept an operation and the client or coordinator can use tunable N, W, and R quorums. W + R > N creates overlap between the acknowledged write and read sets, but overlap alone does not serialize concurrent writes or prove linearizability; version reconciliation, sloppy quorums, membership changes, and failure handling still matter. Read repair and anti-entropy reconcile divergent replicas under product-specific rules.

Do not infer Amazon DynamoDB’s contract from the Dynamo name. DynamoDB does not expose client-selected R and W values. Its API documents eventual or strongly consistent reads per operation for supported single-Region resources, transactional operations with their own contract, and separate consistency/replication choices for global tables. Treat those documented operations and multi-Region modes as the boundary rather than applying the generic quorum formula.

Model Comparison

DimensionSingle-LeaderMulti-LeaderLeaderless
Write scalingBounded by leaderMultiple write pointsAny node
Conflict handlingLeader orders writes; failover still needs fencingMandatoryVersion/conflict handling plus repair
Acknowledgement and readsSync acknowledgement can protect commit durability; replica observation still depends on replay and routingCross-leader acknowledgement and conflict policy are separateQuorum settings influence overlap but do not remove concurrent-write reconciliation
Failover complexityElection, endpoint ownership, and fencingPer-leader failure and conflict recoveryNo leader election, but membership and quorum availability still matter
Typical usePostgreSQL, SQL Server AGCouchDB, geo-distributed write topologiesCassandra, Riak, original Dynamo-style systems

Replication Lag

Asynchronous acknowledgement means the leader does not wait for a standby before confirming a write. A replica may be caught up at that instant or may lag behind the leader. When lag exists, it creates three canonical anomalies (from DDIA Ch. 5):

1. Read-your-writes: a user submits a form, then immediately reloads the page. The read hits a stale replica and the user sees their own write disappear. Fix: route reads to the leader for a short window after a write, or track the LSN of the last write and wait for the replica to catch up. PostgreSQL has no built-in pg_wait_for_lsn; application or custom database logic must poll and compare pg_last_wal_replay_lsn() (or an equivalent replay-position signal) with the required commit LSN. MongoDB exposes causal consistency tokens for the same boundary.

2. Monotonic reads: a user refreshes twice and sees newer data on the first refresh, then older data on the second. Carry the highest observed commit/replay position or a product causal token, then route to the primary or wait for a replica that has reached at least that position. Sticky routing is only an optimization while the selected replica remains available and never moves backward; by itself it does not preserve monotonicity across failover or topology change.

3. Consistent prefix reads: causally related writes appear out of order on a replica. A reply appears before the original message. Preserve the dependency through one ordered commit stream or explicit causal metadata, and read from a replica whose applied position includes the prerequisite. Routing related writes to one partition helps only when that partition supplies the required ordering and failover preserves it; the partition key alone is not a causal guarantee.

Sync vs async tradeoff: synchronous acknowledgement means the leader waits for the configured standby acknowledgement before confirming the write. With the right synchronous_commit, standby selection, and storage settings, that acknowledgement protects commit durability across failover to an eligible standby. It does not make reads from every replica linearizable: WAL can be durable but not yet replayed on a readable standby. A read that must observe the commit still needs the primary or a replica whose replay position has reached the commit token. In asynchronous acknowledgement mode, the leader confirms the write without waiting for a standby. A replica may be caught up or lagging; if the leader fails, an acknowledged commit is at risk only when no eligible surviving node received it.

Replica Read Boundary

Single-leader replication can offload reads only when routing preserves the request’s contract. Writes, write-capable transactions, and unclassified work stay on the primary. An explicitly read-only transaction pins one eligible replica, while a read that must observe a prior commit uses the primary or a replica whose replay position has reached that commit token. A proxy can classify statements, but the application usually owns the causal requirement.

The diagram shows the middleware topology, not the lag or transaction boundary. The routing contract is stricter:

  • A read-only transaction pins one eligible replica until commit or rollback; its statements never hop between nodes.
  • Staleness-tolerant reads may use a healthy replica inside the configured lag budget.
  • A read that must observe a prior write uses the primary or a replica whose replay position reaches the request token.
  • The application or API marks the causal requirement because a SQL proxy usually cannot infer it from statement text.

Position Token

A fixed time window guesses at lag. A position token states the actual boundary:

  1. Commit the write on the primary.
  2. Return a token at or after that commit’s WAL position.
  3. Carry the token with the next read.
  4. Compare each standby’s replay position with the required position.
  5. If replay is behind, wait within a strict budget, route to the primary, or return an explicit consistency failure.
required = 0/16B6C50
replica  = 0/16B6A20  -> wait or primary
replica  = 0/16B6D10  -> eligible

WAL positions are ordered values, not strings to compare lexicographically. A later pg_current_wal_lsn() is a conservative token if concurrent commits advance it; pg_last_wal_replay_lsn() reports a standby’s replay boundary.

The topology image still omits the causal condition: a replica is eligible for a read-after-write request only after replay reaches that request’s token.

Failover and Overload

A token from an old PostgreSQL timeline may not be directly comparable after promotion. The protocol must translate the boundary through failover metadata or route consistency-sensitive reads to the new primary until eligibility can be proved.

Keep waits bounded. An unbounded standby wait turns lag into request exhaustion; routing every lagged read to the primary can overload the node needed for recovery. Track lag, wait duration, primary fallback, and rejected consistency-sensitive reads separately.

FailureRequired behavior
Replica behind tokenBounded wait, primary fallback, or explicit consistency failure
Replica fails during an idempotent readRetry another eligible replica with the same token and retry budget
Primary changes during a transactionFail the pinned transaction; retry the whole transaction only when safe
Commit acknowledgement is lostTreat the result as unknown; resolve by idempotency key or reconciliation

CAP and PACELC

CAP applies when a network Partition splits replicas: the system must choose between Consistency (reject operations that cannot be coordinated) and Availability (keep serving and accept divergence) during that partition. A single-leader system that refuses writes when it cannot reach a quorum is CP; a leaderless Dynamo-style system that keeps accepting writes and reconciles later is AP. Synchronous versus asynchronous durability acknowledgement is a separate contract.

CAP only describes the partition case, which is why PACELC also asks whether normal operation favors latency or coordination. Synchronous commit pays latency for acknowledged durability; it is not by itself a linearizable-replica-read protocol. Strong observation additionally requires routing to the primary or waiting until the chosen replica has replayed the required position. See CAP theorem for the theorem’s full boundary.

Tradeoffs

Replication and sharding solve different problems. Reaching for sharding before exhausting replication is a common over-engineering mistake.

DimensionReplicationSharding
What it solvesRead throughput, HA, DRWrite throughput, dataset size
Write scalingWrites still bottleneck at leaderWrites distributed across shards
Durability and observationAsync acknowledgement can expose an acknowledged commit to failover loss when no eligible survivor received it; sync acknowledgement can protect durability, while replica reads still need replay-aware routingEach shard has its own replication and read-consistency boundary
Operational complexityMediumHigh
When to reach forRead bottleneck, HA requirementWrite or storage bottleneck

Choose each mechanism from the measured bottleneck and required consistency boundary. Read replicas scale eligible reads; a cache removes repeated origin work with a freshness cost; sharding distributes ownership and creates cross-shard work. They are not mandatory stages of one progression.

Pitfalls

Split-brain: a network partition leaves the old primary accepting writes while a new primary is promoted elsewhere. A voting quorum limits who can be elected in the current configuration; fencing is the separate act that revokes the old leader’s ability to write. Promotion must couple both boundaries.

Replication lag as a silent consistency violation: a replica appears healthy in monitoring but is 30 seconds behind. Reads return stale data with no error. Mitigation: monitor replication lag metrics (pg_stat_replication in PostgreSQL, sys.dm_hadr_database_replica_states in SQL Server), alert on lag exceeding your SLA threshold, and expose lag in health checks.

Connection pool not refreshing after failover: infrastructure failover completes in 30 seconds, but the application holds pooled connections to the old primary’s IP. Requests fail until the pool times out. Mitigation: short DNS TTL on the cluster endpoint, connection pool validation on borrow, or a proxy layer (PgBouncer, RDS Proxy, Azure SQL connection retry policy) that handles reconnection transparently.

Replication slot accumulation (PostgreSQL): a replica goes offline but its replication slot remains active. PostgreSQL holds all WAL segments since the slot’s last confirmed LSN, causing disk to fill on the primary. Mitigation: monitor pg_replication_slots for inactive slots, set max_slot_wal_keep_size to cap WAL retention, and drop slots for replicas that have been offline beyond your retention window.

Last-write-wins data loss: in multi-leader or leaderless setups using LWW, concurrent writes to the same key silently discard one of them. No error is returned to the client. Mitigation: use CRDTs for data shapes that support them, or implement application-level conflict detection (version vectors, conditional writes) for critical data like account balances or inventory counts.

Questions

References