CAP and Distributed Systems
A network partition is not a choice — any timeout is one — so the real decision is what a system does while it lasts: refuse writes to stay consistent, or accept writes on both sides and reconcile later; quorums (W + R > N) define the boundary precisely, and PACELC adds the everyday trade the slogan omits: latency versus consistency when nothing is broken at all.
Once data lives on more than one machine, the machines will at some point be unable to talk to each other while clients can still reach both; the system must have decided in advance which guarantee it keeps in that moment, because whichever it does not keep is what its users will experience.
Partitions are not optional
The CAP theorem is usually presented as a menu — consistency, availability, partition tolerance, "pick two" — and the menu is misleading, because partition tolerance is not on offer. A partition is any situation in which two nodes that should communicate cannot, for long enough to matter. Cables get cut, but far more often a switch drops packets for 8 seconds, a garbage collection pause stops a node for 20 seconds, a kernel is starved of CPU, or a link is simply slow enough that every request times out. A timeout is a partition: from the caller’s point of view there is no difference between "the network is down" and "the node is too slow to answer within my deadline", and the caller has to decide what to do without knowing which it is. If your system runs on more than one machine, partitions will happen; the only decision available is what the system does *during* one.
The precise statement is this: during a partition, a system with replicated data must either refuse some operations (typically writes, and sometimes reads, on the side that cannot reach a majority) to keep a single, consistent history — or accept operations on both sides and therefore allow the sides to diverge, to be reconciled afterwards. The first is what CAP calls consistent (CP), the second available (AP). Both are legitimate; neither is free; and the choice can be made per operation, not just per system.
What consistency means here, and what availability costs
The "C" in CAP is linearizability: every operation appears to take effect atomically at some instant between its start and its end, so that once any client has seen a write, every later read anywhere sees it or something newer — the system behaves as if there were one copy. That is a much stronger property than the "C" in ACID, and it is expensive because it requires the replicas to agree before answering. Eventual consistency is the weak end: replicas may return different values for a while, and are guaranteed only to converge once writes stop and communication resumes. Between them sit useful intermediate guarantees — read-your-own-writes, monotonic reads, causal consistency — which Distributed Consistency: CAP, Quorums, Consensus covers in detail; in practice the most common architectures offer linearizability for a few operations (a unique-username check, a balance debit, a leader election) and eventual consistency for the rest.
Choosing availability during a partition is not "free availability". The system that accepts balance = 80 in Europe and balance = 50 in the US must later decide which is right, and there is no general answer: last-writer-wins silently discards one of them, version vectors detect the conflict and hand it to the application, and CRDTs restrict the data types to ones that merge automatically (counters, sets, registers with defined merge rules). Shopping carts merge well — a union of items is acceptable. Bank balances do not, which is why a bank refuses the write instead. The choice is dictated by whether a conflict can be resolved meaningfully after the fact.
Quorums: the boundary made precise
Quorum systems turn the choice into arithmetic. With N replicas, a write is acknowledged after W replicas have it, and a read consults R replicas and takes the newest. If W + R > N, every read set overlaps every write set in at least one replica, so a read is guaranteed to see the latest acknowledged write. With N = 3, W = 2, R = 2, the system tolerates one replica being unreachable for both reads and writes; a side of a partition holding only one replica can serve neither, which is the consistent choice. Set W = 1, R = 1 and both sides keep serving — the available choice — at the cost of stale reads and divergent writes. Sloppy quorums and hinted handoff (Dynamo, Cassandra) let writes land on any W reachable nodes, not the designated ones, and forward them later; this keeps writes available through a partition and weakens the overlap guarantee, so the numbers stop meaning what they appear to mean.
Consensus protocols — Raft in etcd, Consul and CockroachDB, ZAB in ZooKeeper, Paxos in Spanner — are the strict form: a majority must agree on every write in order, and the minority side of a partition cannot make progress at all. That is exactly the property a lock service or a leader election needs: two leaders is worse than no leader.
W=2, R=2 (W+R > N) W=1, R=1 side with 2 replicas reads ✓ writes ✓ reads ✓ writes ✓ side with 1 replica reads ✗ writes ✗ reads ✓ (stale?) writes ✓ (diverge) after healing one history conflict → LWW / vector clocks / CRDT merge
PACELC, and concrete systems
CAP describes the partition, which is rare. PACELC adds the everyday case: if there is a Partition, trade Availability against Consistency; Else, trade Latency against Consistency. A system that stays linearizable in normal operation pays a round trip to a quorum on every write, and often on every read; one that answers from the nearest replica is faster and can be stale. Cross-region synchronous replication is the sharpest example — 80 ms per commit between Frankfurt and Virginia, for every write, forever, in exchange for never losing an acknowledged write in a regional failure. That latency is the price of consistency when *nothing is wrong*, and it is paid far more often than the partition-time price.
What a system actually does during a partition is a matter of its configuration, not its marketing, and most systems can be tuned to either side.
| System | During a partition | Normal operation: latency vs consistency | Notes |
|---|---|---|---|
| PostgreSQL, synchronous replication | Primary blocks commits until a sync standby acknowledges; the minority side (a lone standby) is read-only | Pays one replica round trip per commit for zero-loss failover | Async mode flips it: commits proceed, failover can lose the tail; see Replication and Read Scaling |
| etcd / ZooKeeper / Consul (Raft, ZAB) | Majority side serves reads and writes; minority side refuses writes and, by default, linearizable reads | Every write is a majority round trip; reads can be served locally if staleness is allowed | Designed for locks, leader election and configuration, where two truths are unacceptable |
| Cassandra / DynamoDB-style rings | Tunable: QUORUM refuses on the minority side; ONE keeps both sides writing with hinted handoff | Consistency level per query trades latency for freshness | Conflicts resolved by last-writer-wins (timestamp) unless the application uses versioned reads |
| MongoDB replica set | Majority side elects a primary and continues; minority primary steps down within an election timeout and becomes read-only | w: majority waits for replication; w: 1 is faster and can roll back on failover | Read preference secondary adds stale reads even with no partition |
Kafka (acks=all, min.insync.replicas=2) | A partition whose in-sync set falls below 2 refuses produces; consumers keep reading committed offsets | acks=all costs a replica round trip per batch; acks=1 is faster and can lose the tail on leader failure | Unclean leader election, if enabled, chooses availability and loses data; see Kafka-Style Logs: Topics, Partitions, Offsets |
| Redis Sentinel / Cluster | A minority master keeps accepting writes until min-replicas-to-write or cluster-node timeouts stop it; those writes are lost on failover | Async replication: fast, and a failover can discard acknowledged writes | Fine for caches and ephemeral state; wrong as the only store for money |
| DNS | Every resolver keeps serving cached answers until the TTL expires; fully available, arbitrarily stale | Answers from the nearest cache; propagation takes minutes to hours | The purest AP system in daily use |
Designing for the partition you will have
Because partitions include timeouts, every system that makes a remote call has already made a CAP decision, usually by accident. A payment service that times out on the database and returns an error chose consistency. One that times out and returns "probably succeeded" chose availability and must now reconcile — which is where the idempotency key from the async module and the compensation in Saga Pattern come from; Distributed Transactions is the same problem stated for multi-step writes. Make the decision explicit, per operation: the operations that cannot tolerate divergence (debits, unique names, leader election, inventory reservation) go through a quorum or a single leader and are unavailable on the minority side; everything else (view counts, presence, carts, feeds) accepts writes anywhere and merges.
Then rehearse it. Inject latency and drop links between availability zones in staging and watch what each store does; the failure that surprises you in production is the one you assumed was a theory. Leases should have expiry so a partitioned leader stops leading before a new one is elected (fencing tokens make the old leader’s late writes rejectable). And the minority side should *know* it is the minority: a node that cannot reach a majority should say so, not guess.
- Every remote timeout is a partition; the code path after the timeout is your CAP policy whether or not you wrote it as one.
- Choose per operation, not per system: quorum for the few things that cannot diverge, availability with merge rules for the rest.
- PACELC is the daily bill: synchronous cross-region consistency costs a round trip on every write, all year, not only during outages.
- A partitioned leader must step down on lease expiry, and stale writes must be fenced; otherwise "consistent" is a configuration, not a fact.
Key points
- Partition tolerance is not a choice; any timeout is a partition. The only decision is what the system does during one.
- During a partition: refuse operations on the minority side (consistent), or accept on both sides and reconcile later (available). Choose per operation.
- CAP’s consistency is linearizability — one apparent copy — and it costs a quorum round trip; eventual consistency guarantees only convergence.
- W + R > N gives read/write overlap and tolerates N − max(W, R) unreachable replicas; sloppy quorums and hinted handoff trade that guarantee for availability.
- PACELC: with no partition, the trade is latency versus consistency, and that cost is paid on every write, every day.
A partition: choose consistency or availability
strongly consistent every read returns the latest acknowledged write (linearizable: as if one copy) eventually consistent replicas converge once writes stop flowing; reads in between may be stale quorum rule W + R > N ⇒ the read set intersects the last write set PACELC if Partition: Availability or Consistency; Else: Latency or Consistency
How data moves through it
One request or event, hop by hop.
- 1Client (EU) → Replica 1:
write balance = 80; Replica 1 forwards to Replica 2 and 3 and waits for W acknowledgements. - 2Replica 1 → Replica 2: replication times out — a partition, whatever the cause. With W = 2 and Replica 3 reachable, the write commits; with only Replica 1 reachable, it is refused.
- 3Client (US) → Replica 2:
read balance; with R = 2 and no majority reachable, Replica 2 refuses (consistent) or answers with its last value flagged as possibly stale (available). - 4Link heals → Replicas: missing writes are streamed; if both sides accepted writes, the conflict is resolved by the configured rule or handed to the application.
- 5Leader lease expires → Minority leader: steps down; the majority elects a new leader; writes carrying the old leader’s fencing token are rejected.
When to use — and when not
- Every time data is replicated across machines, zones or regions: decide, per operation, which side of a partition may proceed.
- Choosing a store: read its partition behaviour (leader election, quorum settings, conflict resolution) before its feature list.
- Designing timeouts and error paths in any service that calls another: the post-timeout branch is the CAP policy.
- As a slogan to justify a choice ("we picked AP") without stating what happens to conflicting writes.
- On a single-node system: there is no replication, so there is no partition to reason about — it is simply up or down.
- To reason about latency in normal operation: that is PACELC’s second half, and it usually matters more than the partition case.
Tradeoffs
The rating depends entirely on the side chosen: a quorum system has high consistency and pays latency; an available system has low latency and shifts the complexity into conflict resolution.
How it fails
- Split brain: both sides of a partition elect a leader and accept writes; after healing, two histories exist and one is discarded.
- A "consistent" store configured for availability: Redis with async replication or Kafka with unclean leader election loses acknowledged writes on failover.
- Last-writer-wins with skewed clocks: the "newest" write is the one from the node whose clock is ahead, not the one that happened last.
- Timeout treated as failure for a write that actually committed: the client retries without an idempotency key and the operation happens twice.
- A minority node that keeps serving reads confidently: users see data that the majority has since changed, with no indication it is stale.
How it scales
- Quorum cost grows with replica count and distance; keep N small (3 or 5) and place replicas in zones close enough that the round trip is acceptable.
- Scale out by partitioning the keyspace so each partition has its own small quorum (Consistent Hashing), rather than one large consensus group.
- Multi-region active-active requires conflict resolution by design (CRDTs, per-region ownership of keys); multi-region active-passive keeps one writer and pays failover time instead.
How it interacts with databases, queues, caches, APIs and external systems
- Databases: the replication mode (sync/async), quorum settings and election timeouts are the CAP configuration; see Replication and Read Scaling and Distributed Consistency: CAP, Quorums, Consensus.
- Coordination services: etcd, ZooKeeper and Consul are CP by design and are where locks, leader election and service registries belong; see Service Discovery.
- Message brokers: Kafka’s
acks,min.insync.replicasand unclean-election settings decide what survives a broker partition; see Kafka-Style Logs: Topics, Partitions, Offsets. - Caches: almost always available-and-stale by design; never the system of record for anything that cannot be recomputed; see Caching Architecture.
- Services calling services: every timeout is a partition, so the error path is the policy; the patterns in Reliability Patterns and the compensation in Saga Pattern implement it.