Consistency, CAP, and Replication: The Tradeoffs You Cannot Escape
The moment you keep more than one copy of your data — for durability, for read scaling, for surviving a datacenter loss — you inherit a set of trade-offs that no amount of engineering can make disappear. They are not bugs to be fixed; they are consequences of physics and logic. This post maps those trade-offs: what consistency models actually promise, what CAP and its sharper successor PACELC really say, how the three replication architectures behave, and how real databases pick their spot. The goal is that “eventually consistent” stops being a vague warning label and becomes a precise, costed engineering choice.
This is the second post in a distributed-systems fundamentals series, building on consensus (how replicas agree) and feeding into time and ordering (how to coordinate without a clock).
Replication: the source of all the trouble (and all the benefit)
Replication means keeping copies of the same data on multiple nodes. You do it for three reasons: availability (survive a node or region failure), read scalability (serve reads from many copies), and latency (serve users from a nearby copy). These are real and valuable. The cost is the central question of this entire post: when a write lands on one copy, the others are momentarily out of date — and how you handle that window is the whole problem.
Everything that follows is about that window: how small you make it, who is allowed to see stale data, and what happens when two copies are updated independently.
The consistency spectrum
“Consistency” is not binary. It is a spectrum of guarantees a client can rely on, from strongest (and most expensive) to weakest (and cheapest). Knowing the rungs of this ladder is the single most useful thing in this post.
| Model | Guarantee | Cost |
|---|---|---|
| Linearizable (strong) | Every read sees the most recent write; the system behaves as if there is one copy and operations happen in real-time order | Coordination on every operation; higher latency; unavailable under partition |
| Sequential | All nodes see operations in the same order, but not necessarily real-time order | Slightly cheaper than linearizable |
| Causal | Operations that are causally related are seen in order everywhere; unrelated ops may differ | No global coordination needed; preserves “reply never appears before the message” |
| Read-your-writes / monotonic | Session guarantees: you see your own writes; you never see time go backwards | Cheap, per-client; very high value for UX |
| Eventual | If writes stop, all replicas eventually converge — with no promise about when or what you see meanwhile | Cheapest, most available; weakest |
Two things to internalize:
- Stronger consistency costs coordination, and coordination costs latency and availability. Linearizability is wonderful to program against — the database “just behaves like one machine” — but every operation must coordinate (often via the consensus from the previous post), which adds round-trips and makes the system stop accepting writes during a partition.
- The middle of the spectrum is underrated. Causal consistency and the session guarantees (read-your-writes, monotonic reads) deliver most of what users actually notice — your comment appears after you post it, the page does not show older data after newer — without the full cost of linearizability. A great deal of practical system design lives here.
What “eventually consistent” actually costs you, concretely: a read may return stale data (an old value), and two reads in a row may even go backwards in time (hit different replicas at different freshness) unless you add monotonic guarantees. That is the price for the availability and latency eventual systems give you.
CAP: the theorem everyone quotes and few state correctly
The CAP theorem (Brewer, proved by Gilbert and Lynch) is widely mangled, so here is the precise version. CAP concerns three properties:
- Consistency (here meaning linearizable),
- Availability (every request to a non-failed node gets a response),
- Partition tolerance (the system keeps working despite the network dropping messages between nodes).
The theorem says: when a network partition occurs, you must choose between C and A — you cannot have both. That is the entire claim, and the framing that matters is this: partitions are not optional. In any real distributed system the network will partition, so P is not a choice you get to decline. CAP therefore reduces to a decision you make only during a partition:
Network partition happens (it will).
│
┌─────────┴──────────┐
▼ ▼
Choose C (CP) Choose A (AP)
Refuse requests Answer requests
that can't be from both sides,
made consistent risk stale/divergent
→ unavailable → available but
but correct inconsistent
- A CP system (e.g., a single-leader SQL database with synchronous replication, or etcd) responds to a partition by becoming unavailable on the minority side rather than serve possibly-wrong data — exactly the “minority partition stops on purpose” behavior from the consensus post.
- An AP system (e.g., Cassandra or DynamoDB in their available configurations) responds by staying available on both sides, accepting writes, and reconciling the resulting divergence later.
The classic mistake is treating CAP as a permanent, system-wide label. It is not — it is a behavior during partition. The rest of the time (no partition), CAP says nothing, which is exactly why we need a better framework for the common case.
PACELC: the reformulation you should actually use
CAP’s blind spot is that partitions are rare, yet the trade-off you live with every day is about something CAP ignores: latency. PACELC (Abadi, 2010) fixes this. Read it as:
If Partition (P), choose between Availability and Consistency (A/C); Else (E), choose between Latency and Consistency (L/C).
The “else” clause is the important addition: even with a perfectly healthy network, stronger consistency costs latency, because keeping replicas in agreement requires waiting for them to coordinate. So every system has a two-part personality:
| System | Partition behavior | Normal-operation behavior | PACELC |
|---|---|---|---|
| Classic single-leader SQL (PostgreSQL, sync replica) | favors C (CP) | favors C (pay latency) | PC/EC |
| DynamoDB / Cassandra (tunable, AP default) | favors A (AP) | favors L (low latency) | PA/EL |
| Spanner / CockroachDB | favors C (CP) | favors C (pay latency for strong consistency) | PC/EC |
PACELC is the better lens because it forces the question you actually face on a normal Tuesday: how much latency are you willing to pay for stronger consistency? That is a knob you turn far more often than you handle a partition.
The three replication architectures
How replicas are organized determines everything above. There are exactly three patterns.
Single-leader (primary/replica)
One node is the leader; all writes go to it. The leader streams its changes to follower replicas. Reads can be served by the leader (always fresh) or by followers (possibly stale).
writes
Client ───────► Leader ──┬─► Follower (read replica)
├─► Follower (read replica)
└─► Follower (read replica)
reads can hit leader (fresh) or followers (maybe stale)
- Strengths: simple, no write conflicts (one writer), strong consistency available if you read from the leader or replicate synchronously.
- Weaknesses: the leader is a write bottleneck and a failure point (failover takes time and needs the consensus machinery), and follower reads introduce replication lag — the stale-read window.
- Sync vs async replication is the key tuning knob: synchronous (wait for a follower to confirm before acking the write) gives durability and lets a follower take over without data loss, but adds latency and can stall if the follower is slow; asynchronous is fast but risks losing the last few writes on failover. Most systems do semi-synchronous — one synchronous follower, the rest async.
- Used by: PostgreSQL, MySQL, MongoDB (replica sets), most traditional databases.
Multi-leader
Multiple nodes accept writes (e.g., one leader per datacenter), replicating to each other. Good for multi-region write latency and offline operation, but it creates the hard problem: two leaders can accept conflicting writes to the same key, and you must detect and resolve conflicts (last-write-wins, application merge, or CRDTs from the next post). Used in multi-datacenter setups and at the heart of offline-sync systems.
Leaderless (Dynamo-style)
There is no leader; the client (or a coordinator) writes to many replicas and reads from many replicas, using quorums to get consistency. This is the architecture behind DynamoDB and Cassandra, and it deserves its own section.
Quorum reads and writes: R + W > N
Leaderless systems get consistency from a simple, beautiful inequality. With N replicas, require every write to be acknowledged by W of them and every read to consult R of them. If you choose:
R + W > N
then the set of replicas a read touches must overlap the set the latest write touched — so the read is guaranteed to see at least one copy of the most recent write (and version numbers let it pick the newest). This is the same majority-overlap trick from the consensus post, generalized into a tunable knob.
N = 3 replicas. Choose W = 2, R = 2. R + W = 4 > 3 ✓
Write goes to 2 of 3: [v2][v2][v1]
Read consults 2 of 3: any 2 must include a v2 → fresh read
The power is that you tune the trade-off per operation:
- W=N, R=1: fast reads, slow/fragile writes (every replica must ack).
- W=1, R=N: fast writes, slow reads.
- W + R > N (e.g., quorum each): balanced strong-ish consistency.
- W + R ≤ N (e.g., W=1, R=1): maximum availability and speed, but reads can be stale — the explicitly “eventual” setting.
Cassandra exposes this directly as per-query consistency levels (ONE, QUORUM, ALL, LOCAL_QUORUM…). DynamoDB offers “eventually consistent” (cheaper, faster) vs “strongly consistent” reads as the same dial. This is “eventual consistency” made into a precise, per-call decision rather than a vague property.
A caveat worth knowing: quorums guarantee overlap, but edge cases (concurrent writes, failed writes leaving some replicas updated, sloppy quorums during failures) mean leaderless systems still need read repair and anti-entropy background processes to converge, and they still require conflict resolution for truly concurrent writes.
Session guarantees: the cheap wins that matter most to users
Full linearizability is expensive, but users rarely need it globally. What they do notice are violations of session guarantees — per-client promises that are cheap to provide:
- Read-your-writes: after you update your profile, you see the new version (even if other users briefly do not). Without this, you save a change, the page reloads from a lagging replica, and your change “disappears” — a classic infuriating bug.
- Monotonic reads: you never see time go backwards. Two successive reads will not show newer-then-older data (which happens when consecutive reads hit replicas at different lag). Pinning a client to a replica, or tracking a version watermark, provides this.
- Monotonic writes / writes-follow-reads: your own writes apply in order; a write that depends on something you read happens after it.
These are usually implemented by routing a client’s reads to a sufficiently-fresh replica or carrying a version token. The lesson for system designers: before reaching for global strong consistency, ask whether session guarantees solve the actual user-visible problem — they very often do, at a fraction of the cost.
How real systems choose
Mapping the theory onto tools you use:
- PostgreSQL / MySQL — single-leader, strong consistency on the leader, tunable sync/async replication to followers. PC/EC: correct and consistent, you pay latency and accept follower lag for read scaling. The default for systems that want strong consistency and can live within one primary’s write capacity.
- DynamoDB — leaderless, AP by default with optional strongly-consistent reads; PA/EL. Built for massive availability and predictable low latency, with consistency as a per-read choice.
- Cassandra — leaderless, tunable per-query consistency (the
R/Wdial exposed directly), AP-leaning; PA/EL. Chosen for write-heavy, multi-region, always-available workloads where the app handles the consistency trade-offs explicitly. - Spanner / CockroachDB — distributed SQL that uses consensus (Raft/Paxos) per data range plus careful clock handling (the next post) to offer strong consistency at global scale; PC/EC. They prove you can have strong consistency across regions — but you pay for it in latency and, for Spanner, specialized hardware clocks.
There is no “best” here, only fit. A payments ledger wants PC/EC and will pay the latency. A global shopping cart or activity feed often wants PA/EL and will design around staleness. The skill is matching the data’s actual requirements to a point on these curves.
The tradeoffs you cannot escape, summarized
- Replication buys availability, scale, and latency — at the cost of a staleness window you must manage.
- Consistency is a spectrum. Linearizable is easiest to program against and most expensive; eventual is cheapest and weakest; causal and session guarantees sit in a high-value middle.
- CAP is a narrow claim about partition behavior: during a partition, pick C or A. PACELC is the better daily lens: even without partitions, pick latency or consistency.
- Three replication shapes (single-leader, multi-leader, leaderless) trade simplicity against write-scaling and conflict-handling; R + W > N makes leaderless consistency a tunable dial.
- “Eventually consistent” has a precise cost: stale reads, and non-monotonic reads unless you add session guarantees.
These constraints are permanent. Good distributed-systems design is not escaping them — it is choosing where on each curve your data belongs, deliberately and per workload. The final post in the series tackles the problem lurking under all of this: how do you order events and reconcile concurrent updates when there is no trustworthy global clock?
Sources:
- Brewer’s CAP Theorem / “CAP Twelve Years Later” — Eric Brewer
- Consistency Tradeoffs in Modern Distributed Database Design (PACELC) — Daniel Abadi
- Designing Data-Intensive Applications (replication & consistency) — Martin Kleppmann
- CockroachDB’s consistency model
- Amazon DynamoDB read consistency — AWS docs
Comments