system-design

Consistency Models

What “the data is correct” means when the data lives on five machines — and why the right answer is different for a bank balance and a like counter.

Prerequisites: Scalability, Availability Time to read: ~20 minutes


The problem

One database, one copy of the data: “consistency” is trivial. You wrote 5, you read 5.

Now you have three replicas, because you need availability and read throughput. A write lands on replica A. Half a millisecond later, someone reads from replica C — which hasn’t received it yet.

Time    Replica A       Replica B       Replica C
t=0     balance=100     balance=100     balance=100
t=1     balance=50 ←write
t=2     balance=50      balance=50
t=3                                     balance=50   ← finally
                          ↑
              A read here returns 100. Is that a bug?

It depends entirely on what the data is. If it’s a bank balance and you’re about to authorize a withdrawal, that’s a serious bug. If it’s a like count on a photo, nobody will ever notice or care.

A consistency model is a contract: it defines exactly what a reader is guaranteed to see. And because stronger guarantees require more coordination, they cost latency and availability. Choosing the model is one of the highest-leverage decisions in a design.


🧠 Mental model: the group chat

Five friends in a group chat with bad reception.

Every one of these is a real, named model, and each is the correct answer for some system.


The spectrum, from strongest to weakest

Linearizability (strong consistency)

The guarantee: the system behaves as if there is exactly one copy of the data, and every operation takes effect at a single instant between its start and completion. Once a write completes, every subsequent read — from any client, on any replica — returns that value or a later one.

How it’s achieved: coordination. Either all reads and writes go through one leader, or a quorum of replicas must agree before a write is acknowledged. → Consensus, Quorums

Cost: every write waits for a majority to acknowledge — so latency is bounded by your slowest necessary replica, and cross-region writes cost a full round trip (~150 ms). During a network partition, the minority side must refuse to serve rather than risk divergence.

Use for: account balances, inventory in a flash sale, unique constraints (usernames, seat booking), distributed locks, leader election, anything where a stale read causes money to move incorrectly.

Systems: etcd, ZooKeeper, Google Spanner, single-node relational databases, CockroachDB, FaunaDB.

Sequential consistency

All operations appear in some single global order that every node agrees on, and each individual client’s operations appear in the order they issued them. Weaker than linearizability because that global order need not match real time — an operation that finished before another started can appear after it.

Mostly of theoretical interest; you’ll rarely choose it explicitly, but it’s the standard answer to “what’s weaker than linearizable but still totally ordered?”

Causal consistency

The guarantee: operations that are causally related are seen in the same order by everyone. Concurrent (unrelated) operations may be seen in different orders.

If Bilal posts “I lost my keys” and Ayesha replies “check the car,” nobody will ever see the reply without the original. But two unrelated posts might appear in different orders for different users — and nobody cares.

Why it’s attractive: it’s the strongest model that remains available during partitions. It gives you the guarantee humans actually notice (cause before effect) without global coordination.

How it’s tracked: vector clocks or explicit dependency metadata.

Use for: comment threads, social feeds, collaborative tools, messaging.

Read-your-writes consistency

The guarantee: you always see your own writes. Other users may lag.

This is the one users complain about when it’s missing, and it’s the cheapest fix in this entire chapter. The classic bug:

User posts a comment          → write goes to the primary
Page refreshes                → read goes to a replica (lagging 200 ms)
Comment isn't there           → "the site ate my comment!"
User posts it again           → now there are two

Fixes, cheapest first:

  1. Route reads to the primary for a short window after a write by that user (e.g. 5 seconds).
  2. Pin that user’s session to the primary, or to a specific replica, for a while.
  3. Track the write’s position in the replication log and only read from replicas that have caught up to it.
  4. Read from the primary for data the user can modify; use replicas for everything else.

🎙️ Bringing this up unprompted — “we should handle read-your-writes here, or users will think their post vanished” — is a small, specific, very effective way to demonstrate real experience.

Monotonic reads

The guarantee: you never see time go backwards. If you read a value, a later read won’t show you an older one.

Without it: you refresh a page and a comment appears, refresh again and it’s gone, refresh again and it’s back. This happens when successive requests hit replicas with different lag. Usually fixed by routing a given user consistently to the same replica.

Eventual consistency

The guarantee: if writes stop, all replicas eventually converge to the same value. That’s it. No promise about when, and no promise about what you see in the meantime — including reads that appear to go backwards.

Why anyone accepts this: it’s the cheapest and most available model. Writes are acknowledged immediately by any replica; no coordination, no waiting, and the system keeps serving during partitions.

📐 In practice “eventually” is usually milliseconds to seconds — but under load or partition it can be minutes, and your design must not break when it is.

Use for: view counts, like counts, follower counts, DNS, product recommendations, search indexes, analytics, caches.

Systems: Cassandra and DynamoDB (in their default modes), Riak, DNS, most CDNs.


The comparison

Model Guarantee Coordination cost Available during partition?
Linearizable Reads always see the latest write High — quorum or leader per operation ❌ Minority side must refuse
Sequential One global order, per-client order preserved High
Causal Cause always precedes effect Medium — track dependencies
Read-your-writes You see your own writes Low — routing trick
Monotonic reads Time never goes backwards for you Low — sticky replica
Eventual Converges, someday None

Choosing: the question to ask

For each piece of data in your design, ask: “What is the actual consequence of a reader seeing a value that is 5 seconds old?”

Consequence Model
Money moves incorrectly; two people get the same seat; a duplicate username is created Linearizable
A conversation reads out of order and looks broken Causal
The user thinks their own action failed Read-your-writes
A number is slightly off Eventual
Nobody could possibly notice Eventual

🚨 The critical insight: this is a per-field decision, not a per-system one.

The same e-commerce system, in one request:

Data Model Why
Inventory count at checkout Linearizable Overselling costs real money and angry customers
Payment record Linearizable Obviously
Order status for the buyer Read-your-writes They just placed it and must see it
Product description Eventual Nobody notices a 10-second-old description
“1,247 people viewed this” Eventual It’s a marketing number
Reviews Eventual (causal for replies) Replies must follow their parent
Recommendations Eventual It’s a guess anyway

🎙️ A very strong interview move: “I’d use different consistency models for different parts of this. Inventory and payments need strong consistency, so those go through a linearizable store. The product catalogue and view counts are eventually consistent and can be served from replicas and caches. That gets us strong guarantees only where we pay for them.”

That single answer demonstrates you understand consistency as an engineering trade rather than a setting.


Where the “consistency” in ACID fits

Confusingly, the C in ACID means something different from the C in CAP or from this chapter.

They are unrelated concepts that share a word. Knowing this distinction is a common interview follow-up, and getting it right is a nice signal. → Transactions


⚖️ Trade-offs

Choice Gain Cost
Strong consistency Simple reasoning; correctness by default Higher latency (quorum/leader round trips); unavailable during partitions; harder to scale writes
Eventual consistency Low latency, high availability, easy scaling Application must tolerate stale and out-of-order reads; conflict resolution becomes your problem
Causal consistency Preserves what users notice Metadata overhead (vector clocks) and more complex implementation
Read-your-writes only Fixes the complaint users actually have, cheaply Doesn’t help cross-user staleness
Mixed per-field Pay only where it matters Two code paths; developers must know which is which

In the real world


🚨 Interview traps


🎙️ Soundbites


🛠️ Try it

1. See replication lag. Run Postgres with a streaming replica (Docker makes this a 10-minute job). Write to the primary and immediately read from the replica in a tight loop. Measure how often you get stale data, and how stale. Then put the replica under load and measure again — lag grows exactly when you least want it to.

2. Break read-your-writes on purpose. Build a two-endpoint app: POST /comment writes to the primary, GET /comments reads from the replica. Add 200 ms of artificial replication delay. Use it. You’ll immediately feel why users report “the site ate my comment.”

3. Classify a real system. Open any app you use. List ten pieces of data on one screen. For each, write down which consistency model it needs and what the worst case is if it’s 5 seconds stale. You’ll find that almost everything is fine with eventual, and one or two things absolutely aren’t.


Check yourself

1. A user posts a comment and it doesn't appear on refresh. What consistency guarantee is missing, and what's the cheapest fix? Read-your-writes. The write went to the primary and the read hit a lagging replica. Cheapest fix: route that user's reads to the primary for a short window after their write (a session flag with a 5-second TTL, or a cookie recording the write timestamp). More rigorous: record the write's replication log position and only serve that user from replicas that have caught up past it.
2. Why is causal consistency often described as the sweet spot? Because it's the strongest model that can remain available during a network partition, and it captures the ordering that humans actually perceive — cause before effect. Users don't notice that two unrelated posts appeared in different orders on different devices, but they absolutely notice a reply showing up before the message it replies to. So you get the visible correctness of a strong model without the coordination cost or the availability sacrifice.
3. Give one field in a social media app for each of: linearizable, read-your-writes, eventual. **Linearizable:** username registration (two people must not claim the same handle), or account balance in a payments feature. **Read-your-writes:** the user's own posts, comments, and profile edits — they must see their own actions immediately. **Eventual:** like counts, follower counts, view counts, trending topics, and recommendations — all of which can be seconds stale with no user-visible harm.
4. What does the C in ACID mean, and why is it confusing? It means the database enforces declared invariants — constraints, foreign keys, uniqueness — so a transaction takes the database from one valid state to another. It's about application-level correctness rules on a *single* database. This is completely unrelated to distributed consistency (what replicas show readers), which is what the C in CAP means. Same word, different concepts, and interviewers like to check whether you know that.
5. Your system uses a primary with three read replicas and you tell the interviewer it's "strongly consistent." What's wrong? Reads served from replicas are not strongly consistent — replicas lag the primary, so a read can return a value older than the most recent committed write. You have strong consistency only for reads served by the primary (or by a quorum). To claim it while using replicas you'd need to either route all reads to the primary (losing the read-scaling benefit), use synchronous replication (which makes writes slower and reduces availability), or use quorum reads. The honest statement is: strong for primary reads, eventual for replica reads — and then say which data goes where.

Further reading