Consistency Models
One-line summary: A consistency model is the contract between a distributed store and its clients about when and in what order writes become visible to reads.
๐งฉ Core Conceptsโ
When data is replicated across nodes (see Replication), reads and writes can race. A consistency model defines what guarantees you get. Stronger models are easier to reason about but cost latency and availability (recall the CAP theorem and PACELC's else-latency-vs-consistency trade-off).
๐ The Consistency Spectrumโ
From strongest (most coordination, highest latency) to weakest (most available, lowest latency):
flowchart LR
Strong["Strong / Linearizable<br/>latest write, global order"]
Seq["Sequential<br/>one global order, not real-time"]
Causal["Causal<br/>cause-before-effect preserved"]
Eventual["Eventual<br/>converges given no new writes"]
Strong --> Seq --> Causal --> Eventual
Strong -. "more coordination / higher latency" .-> Strong
Eventual -. "more available / lower latency" .-> Eventual
| Model | Guarantee | Cost | Example |
|---|---|---|---|
| Strong (Linearizable) | Reads always return the most recent write; behaves like a single copy in real time | Highest latency, lowest availability | etcd, ZooKeeper, Spanner |
| Sequential | All nodes see operations in the same order, but not necessarily real-time order | High | Some replicated logs |
| Causal | Operations that are causally related are seen in order; concurrent ops may differ | Medium | COPS, MongoDB causal sessions |
| Eventual | If writes stop, all replicas eventually converge | Lowest | DynamoDB, Cassandra, DNS |
๐ค Client-Centric (Session) Guaranteesโ
Even under eventual consistency, these per-client guarantees make behavior sane for a single user session:
- Read-your-writes: after you write a value, your subsequent reads see it (never a stale value you just overwrote). Example: after editing your profile, you see the update immediately.
- Monotonic reads: once you read a value, you never see an older value on later reads. No "time-travel backwards."
- Monotonic writes: your writes are applied in the order you issued them.
- Writes-follow-reads: a write made after reading a value happens-after that value.
sequenceDiagram
participant U as User (one session)
participant R1 as Replica A
participant R2 as Replica B
U->>R1: write(name = "Ada")
R1-->>U: OK
U->>R2: read(name)
Note over R2: Without read-your-writes:<br/>may return old value โ
Note over R2: With sticky session /<br/>version tracking: returns "Ada" โ
๐ณ๏ธ Quorum Reads & Writes (W + R > N)โ
Dynamo-style stores tune consistency with three numbers:
- N = number of replicas that store each key
- W = replicas that must acknowledge a write before it's considered successful
- R = replicas that must respond to a read before returning
Strong-consistency rule of thumb: if W + R > N, the read and write quorums overlap by at least one node, so every read is guaranteed to see the latest write.
flowchart TD
subgraph N["N = 3 replicas"]
A[Replica 1]
B[Replica 2]
C[Replica 3]
end
W["Write quorum W=2"] --> A
W --> B
R["Read quorum R=2"] --> B
R --> C
B -. "overlap guarantees latest value" .-> R
| Config (N=3) | W + R | Property |
|---|---|---|
| W=3, R=1 | 4 > 3 | Strong; fast reads, slow/less-available writes |
| W=1, R=3 | 4 > 3 | Strong; fast writes, slow reads |
| W=2, R=2 | 4 > 3 | Balanced strong consistency (common default) |
| W=1, R=1 | 2 < 3 | Eventual; fastest & most available, may read stale |
๐ Conflict Resolutionโ
When concurrent writes diverge (common in AP / multi-leader systems), replicas must reconcile:
- Last-Write-Wins (LWW): keep the write with the highest timestamp. Simple but loses data on true concurrency and depends on clock sync. Used by Cassandra by default.
- Vector clocks: track causality per replica to detect whether writes are ordered or truly concurrent; concurrent writes are surfaced as siblings for the app (or a merge function) to resolve. Used by Riak / Dynamo.
- CRDTs (Conflict-free Replicated Data Types): data structures (counters, sets, maps) mathematically designed so concurrent updates always merge deterministically without conflicts. Used by Redis CRDTs, Riak, collaborative editors.
flowchart TD
Conflict{"Concurrent writes<br/>detected?"}
Conflict -->|LWW| L["Pick highest timestamp<br/>โ ๏ธ may drop a write"]
Conflict -->|Vector Clocks| V["Detect causal vs concurrent<br/>expose siblings to app"]
Conflict -->|CRDT| D["Auto-merge deterministically<br/>โ
no data loss"]
Vector clock exampleโ
Replica A writes: {A:1}
Replica B writes: {B:1} โ concurrent (neither dominates) โ sibling / merge
Replica A reads {A:1,B:1}, writes: {A:2,B:1} โ dominates {A:1} โ supersedes
๐๏ธ Tunable Consistencyโ
Modern stores let you choose the model per request, dialing between availability/latency and correctness:
| Store | How to tune |
|---|---|
| Cassandra | Per-query consistency level: ONE, QUORUM, LOCAL_QUORUM, ALL |
| DynamoDB | ConsistentRead: true for strong reads, else eventual |
| MongoDB | writeConcern (w=majority) + readConcern (majority/linearizable) + readPreference |
| Riak | Per-bucket / per-request n_val, r, w |
This lets a single application use strong consistency for money and eventual consistency for analytics/feeds โ the right trade-off per operation.
โ๏ธ Trade-offs / When to Useโ
- Strong / linearizable: use for correctness-critical data โ balances, inventory, locks, unique constraints. Accept higher latency and reduced availability under partitions.
- Causal: great sweet spot for social apps โ preserves "reply appears after the post" without full linearizability cost.
- Eventual + session guarantees: use for high-scale, latency-sensitive workloads (feeds, carts, telemetry) where users mainly need to see their own actions consistently.
- Quorum tuning (W+R>N): your lever to move along the spectrum without changing databases.
- Conflict strategy matters: prefer CRDTs/vector clocks over LWW when losing concurrent writes is unacceptable.
Interview Questionsโ
- Compare linearizability vs eventual consistency and give examples where each is appropriate.
- How would you design a shopping cart so users don't lose their items while keeping high availability?
- Explain how quorum parameters (W, R, N) affect consistency and latency.
Production Checklistโ
- Define which operations require strong consistency vs eventual consistency and document per-endpoint guarantees
- Instrument client-centric session guarantees where needed (read-your-writes, monotonic reads)
- Ensure clocks are reasonably synchronized (NTP) if using timestamp-based conflict resolution
- Provide clear API documentation stating consistency expectations
Testing & Monitoringโ
- Run tests that assert client guarantees under leader failover and replication lag
- Simulate conflicts and verify chosen conflict-resolution strategies (LWW vs vector clocks vs CRDTs)
- Monitor per-operation latency, error rates, and observed staleness windows
๐ Related Topicsโ
- CAP Theorem โ the C-vs-A trade-off these models refine
- Replication โ leader/follower & quorum mechanics that implement these models
- Databases โ which stores offer which consistency guarantees
- Message Queues โ delivery guarantees (at-least/at-most/exactly-once) as a consistency analog
โ Back to System Design ยท ยฉ sparshjaswal