Step 07 of 22
Consistency & CAP
Know what linearizable, causal and eventual consistency guarantee, what CAP and PACELC really say, and how to choose a guarantee per operation instead of per system.
"Is this database consistent?" has no useful answer until you say consistent how. Once data lives on more than one machine, a read can return the latest write, an older one, or a mix, and different systems promise different things. This lesson gives you precise names for those promises, explains what CAP and PACELC actually claim (it's less than people think), and shows how to pick the guarantee each feature needs.
Two meanings of "consistent"
The word does double duty:
- In ACID, consistency means a transaction moves the database from one valid state to another: constraints, foreign keys and invariants hold. It's about rules on data.
- In distributed systems, consistency means what readers can observe when data is replicated: whether every replica agrees, and which writes a read is guaranteed to see. It's about timing and order.
This lesson is about the second meaning. A consistency model is a contract: it lists which results a read may legally return. Stronger models allow fewer results — easier to program against, more expensive to provide.
The models, strongest to weakest
See consistency models for a one-page summary.
Linearizable (strong) consistency. The system behaves as if there were a single copy of the data and every operation took effect at one instant between its start and end. Once a write completes, every later read — from any client, on any replica — returns it or something newer. No client ever sees a value "go back".
You need linearizability whenever correctness depends on everyone agreeing right now:
- Uniqueness: two users must not both claim the username
ana. - Locks and leader election: exactly one holder at a time.
- Money: a balance check and debit must see the latest balance.
Sequential consistency. All clients see operations in the same order, and each client's own operations appear in the order it made them — but that order needn't match real time. A read may return a slightly stale value, as long as everyone agrees on the sequence.
Causal consistency. If one operation could have influenced another — a reply to a comment, an update after a read — everyone sees them in that order. Unrelated operations may be seen in different orders by different clients. It's about the strongest model a system can provide while staying available during a network partition, which makes it attractive for geo-distributed systems.
Session guarantees. Weaker, per-client promises you can layer on top of eventual consistency: read your writes, monotonic reads (never go back in time), monotonic writes and writes follow reads. The replication lesson showed how to provide them by routing. They fix most user-visible anomalies cheaply. See replication lag anomalies.
Eventual consistency. If writes stop, all replicas eventually converge to the same value. That's all: no bound on how long "eventually" is, and in the meantime a read may return any recent value. It's cheap and always available, and it's fine for data where brief disagreement is harmless — like counts, view counts, recommendations, presence indicators.
What CAP actually says
The CAP theorem is usually quoted as "consistency, availability, partition tolerance — pick two". The precise statement is narrower and more useful. See CAP theorem.
When the network partitions — some nodes can't reach others — a system must choose between consistency (linearizability) and availability (every request to a non-failed node gets a non-error response).
Partitions are not optional: switches fail, cables get cut, cloud zones lose connectivity, and long GC pauses look like partitions to everyone else. So "CA" isn't a real choice for a distributed system. The real question is what your system does during a partition:
- A CP system refuses requests it can't serve linearizably — typically the side of the partition without a majority. etcd, ZooKeeper, Consul, and databases using Raft or Paxos behave this way.
- An AP system keeps answering on both sides and reconciles afterwards. Cassandra and DynamoDB at their default settings, and DNS, behave this way.
Two caveats. CAP's "consistency" is only linearizability and its "availability" is the strict every-node version, so many real systems are neither fully CP nor fully AP. And CAP says nothing about the 99.9% of the time when there is no partition.
PACELC: the trade-off you make every day
PACELC fills that gap. See PACELC.
If there is a Partition, choose Availability or Consistency; Else, choose Latency or Consistency.
Even with a healthy network, linearizability costs latency: a write must reach a quorum or a leader before it's acknowledged, and a linearizable read must confirm it's seeing the latest value. Across regions that's tens to hundreds of milliseconds per operation. So:
| System | Partition | Else | Notes |
|---|---|---|---|
| Spanner, CockroachDB | C | C | Pay cross-replica latency on every commit for strong guarantees |
| Cassandra, DynamoDB (defaults) | A | L | Fast and available; reads may be stale |
| PostgreSQL + async replicas | C on the primary | L on replicas | Strong if you read from the primary, eventual from replicas |
Many systems let you choose per request. Cassandra's consistency levels (ONE, QUORUM, LOCAL_QUORUM, ALL) set how many replicas must answer. DynamoDB reads are eventually consistent unless you pass ConsistentRead=true, which costs twice the read capacity. ZooKeeper reads can be stale unless the client calls sync first.
-- cqlsh: this session's reads and writes wait for a majority of replicas in the local data centre.
-- (Drivers set the same thing per statement.)
CONSISTENCY LOCAL_QUORUM;
SELECT balance FROM accounts WHERE id = 7;Choosing a guarantee per operation
The most useful shift is to stop asking "is our system consistent?" and ask, for each operation, what goes wrong if this read is stale, and for how long is that acceptable?
| Operation | Stale read causes | Guarantee |
|---|---|---|
| Claim a username or seat | Two winners | Linearizable (unique constraint on a single leader, or consensus) |
| Debit an account | Overdraft | Linearizable within a transaction |
| Show my own just-saved profile | Confused user | Read-your-writes |
| Comment thread | Reply before the question | Causal (or keep a thread on one partition) |
| Like count, view count | Off by a few for a few seconds | Eventual |
| "Who's online" | Slightly wrong dot | Eventual |
Most of an application can be eventually consistent with session guarantees. Keep the strongly consistent core — money, inventory, identity, uniqueness — small, and put it on a store that provides linearizability properly. The ID and coordination lesson covers the consensus systems that do.
Practice
For a ticketing app that sells concert seats, assign a consistency guarantee to each operation and say how you'd provide it: (1) reserving a specific seat; (2) the "seats remaining" number on the event page; (3) a user's list of their own tickets right after purchase; (4) the payment record; (5) notifications that a friend bought tickets to the same show.
Show solution
- Reserving a seat: linearizable. Two buyers must never both get seat 14C. Provide it with a single authoritative store for seat state and an atomic conditional write:
UPDATE seats SET holder = $user, held_until = now() + interval '10 minutes' WHERE event_id = $e AND seat = '14C' AND (holder IS NULL OR held_until < now()), checking that one row was updated. Reads that decide the reservation go to the primary, never a replica. - "Seats remaining": eventual. A number a few seconds stale is harmless as long as the reservation itself is correct. Serve it from a cache or replica with a short TTL, or compute it asynchronously.
- My tickets after purchase: read-your-writes. The buyer must see the ticket they just bought. Route that user's reads to the primary for a short window after purchase, or check the replica's position against the write's log position.
- Payment record: linearizable and durable. Write it in the same transaction as the reservation confirmation, on the primary, with synchronous replication to a standby so it survives a failover. Use an idempotency key so a retried payment isn't recorded twice.
- Friend notifications: eventual (ideally causal). Delivered asynchronously through a queue. Delays of seconds are fine; the only ordering worth preserving is that a "bought tickets" notification never arrives before the purchase is visible, which delivering after commit achieves.
Key takeaways
- In distributed systems, consistency describes which values a read may return; stronger models allow fewer.
- Linearizability means "one copy, instant effect" and is required for uniqueness, locks and money.
- Causal consistency and session guarantees fix most user-visible anomalies far more cheaply.
- CAP: during a partition, choose between answering and being linearizable. Partitions aren't optional.
- PACELC: without a partition, you still trade latency for consistency on every operation.
- Choose a guarantee per operation, keep the strongly consistent core small, and use atomic conditional writes instead of read-then-write.
Check yourself
Quiz
0/4 correct · 4 left
1.Which statement of CAP is accurate?
2.A reply to a comment sometimes appears before the comment itself. Which is the weakest model that rules this out?
3.What does the 'ELC' part of PACELC describe?
4.On an eventually consistent store, code reads the stock level and, if it's positive, writes stock − 1. What is the risk and the fix?
Concepts in this lesson
One-page notes on each idea, for review or a quick lookup later.
- Quorum reads and writesWith N replicas, R + W > N makes every read overlap the latest write.
- CAP theoremDuring a network partition, choose between answering and being linearizable.
- PACELCCAP covers partitions; PACELC adds the latency-versus-consistency trade-off the rest of the time.
- Consistency modelsLinearizable, sequential, causal and eventual consistency, from strongest to weakest.
Finished reading?
Mark it complete to fill in your roadmap.