Lesson 36 of 129
Consistency Models
A consistency model is a contract between a data store and its clients. It defines which values a read may return given the history of writes, when replicas hold multiple copies of the data and updates propagate with delay. Stronger models are easier to program against but require more coordination, which costs latency and availability (see CAP and PACELC).
Why models are needed
With replication, a write reaches replicas at different times. A reader connected to a lagging replica can observe old data. A consistency model states exactly which anomalies clients may see. Without that statement, application correctness cannot be reasoned about.
Strong models
Linearizability
Linearizability (also called strong or atomic consistency) makes every operation appear to take effect instantaneously at some point between its invocation and its response. Two rules follow:
- All operations are consistent with a single total order.
- That order respects real time: if operation A completes before operation B begins, A precedes B in the order.
After a write completes, every subsequent read, from any client on any replica, returns that value or a later one.
A read that overlaps (is concurrent with) a write may return either the old or the new value. Once any read has returned the new value, every read that begins after that read completes must also return the new value. A history where a read returns x = 2 and a later, non-overlapping read returns x = 1 is not linearizable, even if each replica is internally consistent.
sequenceDiagram
participant A as Client A
participant S as Store
participant B as Client B
A->>S: write x = 1
S-->>A: ok (completed)
B->>S: read x
S-->>B: 1 (must not return the old value)Implementation requires coordination on every operation: a single leader serving reads and writes, or a consensus protocol (Raft) in which reads also go through the leader with a check that it is still the leader. Linearizability is needed for locks, leader election, unique constraints, and compare-and-set operations. Examples: etcd, ZooKeeper writes, Spanner.
Sequential consistency
Sequential consistency requires a single total order of all operations that preserves each client's own program order. Unlike linearizability, the order need not match real time. A write that completed at 10:00 may appear after a read that began at 10:01, as long as every client sees the same order and its own operations stay in order.
Sequential consistency is cheaper to implement but is not composable: two sequentially consistent objects used together may not yield a sequentially consistent system. Linearizability is composable.
Serializability versus linearizability
These terms are distinct. Serializability is an isolation property of multi-operation transactions: the result equals some serial execution of the transactions, with no real-time requirement. Linearizability is a recency guarantee for single-object operations. A system providing both is called strict serializable. Spanner provides this.
Causal consistency
Causal consistency guarantees that operations that are causally related are seen by all clients in the same order. Concurrent operations, with no causal relationship, may be seen in different orders by different clients.
Operation A causally precedes B when:
- A and B occur in the same client session and A is first.
- B reads a value written by A.
- A precedes C and C precedes B (transitivity).
Example: a user posts a question, another user reads it and posts an answer. Under causal consistency, no client sees the answer without the question.
graph LR
Q["post: question"] --> An["post: answer"]
Q --> C["post: comment on question"]
An -.-|"concurrent, any order"| CCausal ordering is tracked with metadata such as vector clocks or dependency lists attached to writes. A replica delays making a write visible until the writes it depends on are visible. Causal consistency is among the strongest models that can remain available during a partition, provided each client keeps using replicas that reflect its own history: it does not require a single leader. Examples: MongoDB causally consistent sessions, COPS and similar research systems.
Eventual consistency
Eventual consistency guarantees only that if no new updates are made, all replicas eventually converge to the same value. It defines no bound on the time and no ordering during convergence. Reads may return stale or out-of-order values.
- Convergence needs a conflict resolution rule for concurrent writes: last-write-wins by timestamp, application merge, or CRDTs.
- Replicas exchange updates through mechanisms such as read repair and anti-entropy (quorums and replication protocols).
- Examples: DNS, Cassandra and Dynamo-style stores at low consistency levels, asynchronous read replicas.
Client-centric session guarantees
Eventual consistency alone produces confusing behavior for one user. Session guarantees are guarantees offered to one client session, regardless of which replicas it contacts.
| Guarantee | Statement | Anomaly prevented |
|---|---|---|
| Read-your-writes | A read in a session sees all earlier writes from the same session | User updates a profile, reloads, and sees the old value |
| Monotonic reads | Once a session has read a value, later reads never return an older value | Content appears, then disappears on refresh because the next request hit a lagging replica |
| Monotonic writes | Writes from a session are applied in the order issued | An update is applied before the insert that precedes it |
| Writes-follow-reads | A write is ordered after the writes whose results were read in that session | A reply becomes visible before the post it answers |
Together, these four guarantees are equivalent to causal consistency.
Session guarantees are scoped to a session. Two sessions of the same user (for example a phone and a laptop) do not share guarantees unless the version token or session identity is shared between them.
Implementation techniques
- Sticky sessions. Route a client to one replica. This provides read-your-writes and monotonic reads until that replica fails.
- Version tokens. The client stores the latest version, log position, or timestamp it has seen and sends it with each request. A replica serves the read only if it has caught up to that version, otherwise it waits or forwards the request.
- Read from the leader after a write. For a short window after a user writes, serve that user's reads from the leader or from a replica known to have the write.
Comparing the models
| Model | Ordering guarantee | Real-time bound | Available under partition | Typical latency cost |
|---|---|---|---|---|
| Linearizable | Single total order | Yes | No (minority side) | Highest, coordination on every operation |
| Sequential | Single total order | No | No | High |
| Causal | Order of causally related operations | No | Yes | Moderate, dependency tracking |
| Session guarantees | Per-session order | No | Yes, with sticky routing | Low |
| Eventual | Convergence only | No | Yes | Lowest |
graph TD
L["Linearizable"] --> S["Sequential"]
S --> C["Causal"]
C --> SG["Session guarantees"]
SG --> E["Eventual"]Each arrow points to a weaker model. A store that satisfies a model also satisfies the models below it in this hierarchy, up to the definitions above.
Choosing a model
- Use linearizable operations for a narrow set of data where correctness depends on recency: locks, leader leases, unique username claims, balances checked before deduction.
- Use causal or session guarantees for user-facing data such as comments, messages, and profile edits.
- Use eventual consistency for data where staleness is harmless: counters shown in a UI, recommendations, caches.
- Keep the strong path small. Using linearizable operations for all data reduces throughput and raises latency to the cost of a quorum round trip on every request.
Interview questions
- What is the difference between linearizability and serializability? Linearizability applies to single-object operations and requires real-time ordering. Serializability applies to multi-object transactions and requires only equivalence to some serial order.
- How do you provide read-your-writes on asynchronous replicas? Route the user's reads to the leader for a period after a write, or attach a version token to the session and serve reads only from replicas that have reached it.
- Why is causal consistency considered the practical middle ground? It preserves cause-and-effect ordering that users notice, and it can be implemented without a single leader, so it stays available during partitions.
- Does eventual consistency mean data can be lost? Not by itself. It means replicas converge. Data loss occurs only if the conflict resolution rule discards a concurrent write, as last-write-wins can.
