Data Consistency
In a Nutshell
Data consistency is the guarantee that everyone reading your data sees a correct, agreed-upon view of it — even when that data is copied across many machines and being updated concurrently. In a single-node database this is trivial; in a distributed system it becomes one of the hardest problems, because replicas take time to sync and networks fail. The central tension, framed by the CAP theorem, is that during a network partition you must choose between staying consistent (reject requests until you can guarantee correctness) and staying available (serve possibly-stale data). This topic looks at consistency through the reliability lens: what guarantees you can offer, and how they interact with availability.

How It Actually Works
The Consistency Spectrum
Consistency isn't binary — it's a spectrum from weak (cheap, fast, available) to strong (expensive, slower, correct):
weak ◀─────────────────────────────────────────────────▶ strong
Eventual Read-your-writes Monotonic Causal Linearizable
│ │ │ │ │
cheapest session no going respects every read
most guarantees backward cause→ sees latest
available effect write (as if
single copy)
| Model | Guarantee | Typical Use |
|---|---|---|
| Strong (Linearizable) | Every read sees the most recent write | Payments, inventory, locks |
| Causal | Causally-related operations seen in order | Comment threads, collaborative apps |
| Read-your-writes | You always see your own updates | Profile edits, settings |
| Monotonic reads | Time never goes backward for a reader | Feeds, timelines |
| Eventual | All replicas converge eventually | Likes, view counts, DNS |
Strong vs Eventual: The Core Trade-off
| Strong Consistency | Eventual Consistency | |
|---|---|---|
| Read freshness | Always latest | May be stale briefly |
| Latency | Higher (coordination) | Lower |
| Availability during partition | Lower (may reject) | Higher (keeps serving) |
| Complexity for app | Simple to reason about | App must tolerate staleness |
| Examples | Spanner, CockroachDB, etcd | Cassandra, DynamoDB (default), DNS |
Strong consistency requires nodes to coordinate on every operation (consensus/quorum), which costs latency and reduces availability during partitions. Eventual consistency lets each replica act independently and reconcile later — fast and available, but the application must handle temporarily-stale reads.
Achieving Strong Consistency
- Consensus (Raft/Paxos): a majority must agree before a write commits (see Consensus & Leader Election).
- Quorum reads/writes: with N replicas, require
Wacks to write andRto read whereR + W > Nguarantees an overlap that sees the latest write.
Quorum example: N=3, W=2, R=2 → R + W = 4 > 3 = N ✅
Write must reach 2 of 3 nodes.
Read must query 2 of 3 nodes → at least one has the latest write.
Handling Eventual Consistency's Conflicts
When replicas accept independent writes, they can diverge and must reconcile:
| Technique | How It Resolves Conflicts |
|---|---|
| Last-Write-Wins (LWW) | Highest timestamp wins (simple, can lose data) |
| Version vectors | Track causal history, detect concurrent writes |
| CRDTs | Data types that mathematically merge without conflict |
| Application merge | App logic decides (e.g., shopping cart union) |
Consistency Is a Per-Feature Decision
The pragmatic reality: you don't pick one consistency model for the whole system — you pick per data type based on the cost of being wrong.
Within ONE e-commerce app:
Payment/balance → STRONG (double-charge is unacceptable)
Inventory count → STRONG-ish (oversell must be bounded)
Product reviews → CAUSAL (replies must follow their parent)
"Likes" count → EVENTUAL (a stale count for 2s is fine)
Recommendations → EVENTUAL (staleness is invisible to users)

Seeing It in Action
Scenario: Read-your-writes consistency in a social app with eventual-consistency storage.
# The store is eventually consistent (fast, available), but users must
# always see their OWN posts immediately, or the app feels broken.
def create_post(user_id, content):
write_ts = store.write(f"posts:{user_id}", content) # async-replicated
# Remember the version this user just wrote
session[user_id]["last_write_ts"] = write_ts
def read_feed(user_id):
required_ts = session[user_id].get("last_write_ts", 0)
# Read from a replica, but require it to be caught up to the user's write
return store.read(f"posts:{user_id}", min_version=required_ts)
# If the chosen replica is behind required_ts, the client retries another
# replica or falls back to the primary — guaranteeing read-your-writes.
What this achieves: the system keeps the low latency and high availability of eventual consistency for the 99% case, while giving each user a strong guarantee about their own writes — the specific consistency property that makes the app feel correct. This is the essence of choosing consistency per-feature rather than globally.
Interview Questions
Q: Explain the trade-off between strong and eventual consistency. Hint: Strong = every read sees the latest write, simple to reason about, but requires cross-node coordination → higher latency and lower availability during partitions. Eventual = replicas serve independently and converge later → low latency and high availability, but reads can be stale and the app must tolerate that. It's fundamentally the CAP trade-off (C vs A during partitions).
Q: How do quorum reads/writes provide strong consistency, and what's the rule? Hint: With N replicas, require W nodes to acknowledge a write and R nodes for a read. If R + W > N, the read and write node sets must overlap, so at least one node in any read has the latest write. E.g., N=3, W=2, R=2. Tuning R and W trades read vs write latency and availability.
Q: Give examples of consistency models between strong and eventual. Hint: Causal (causally-related ops seen in order — replies after their parent), read-your-writes (you always see your own updates), monotonic reads (a reader never sees time go backward). These "session guarantees" are cheaper than full linearizability but fix the most jarring anomalies of pure eventual consistency.
Q: In an eventually-consistent system, how are conflicting concurrent writes resolved? Hint: Last-write-wins by timestamp (simple but can silently drop data), version vectors (detect concurrency and surface conflicts), CRDTs (data types that merge deterministically without conflict), or application-level merge (e.g., union a shopping cart). The right choice depends on whether losing a write is acceptable.
Q: Should a whole system use one consistency model? How do you decide? Hint: No — choose per data type by the cost of being wrong. Money/inventory/locks need strong consistency; comment threads need causal; likes/view counts/recommendations tolerate eventual. Using strong consistency everywhere wastes latency and availability; using eventual everywhere breaks correctness-critical features.
References
- Designing Data-Intensive Applications by Martin Kleppmann — Chapters 5 & 9: Replication and Consistency
- Jepsen: Consistency Models — a rigorous map of the consistency landscape
- Amazon Dynamo paper — quorums and eventual consistency in practice
Dive Deeper
- Consistency Models (Kyle Kingsbury / Aphyr) — interactive hierarchy of models
- Google Spanner paper — strong consistency at global scale via TrueTime
- CRDTs: A Comprehensive Study — conflict-free replicated data types