Strong consistency means a replicated system behaves like a single copy: once a write is acknowledged, every later read sees it. Eventual consistency only promises that replicas agree once writes stop, and nothing about what you read meanwhile. Most features need something in between, such as read-your-writes or causal order, which costs far less than strong.
Picture it first: noticeboards in a housing society
A housing society keeps its master noticeboard in the office. A helper copies each new notice to the lobby board of every tower, so lobby boards run minutes behind, an hour in a busy week. Residents read their own lobby’s board because it is close. Then small dramas happen:
- Your complaint vanishes. You pin a complaint at the office, walk back to your lobby, and it isn’t there. You pin it again.
- The schedule goes backwards. Tower A’s board has the water-cut notice; Tower C’s doesn’t yet. For a moment the cut seems cancelled.
- The answer arrives before the question. Tower D shows the secretary’s reply, “Yes, moved to Sunday”, but not the question it answers.
The strict fix is to make everyone read the office board: always correct, but there is a queue, and when the office is locked nobody can read anything. The helper promises only that if people stop posting, every board ends up matching the office, never when.
The cheap fixes are homely: pin your notice in your own lobby first, number the notices so you can spot an old board, and never pin a reply before its question.
Map it back: the office board is the leader, the lobby boards are read replicas, the helper is asynchronous replication and the notice numbers are versions or log positions. The dramas are the anomalies in this article; the locked office is the price of making everything strong.
Strong and eventual, precisely
Strong usually means linearizable. The system acts as if each object had one copy and each operation took effect at one instant between its start and finish. Once a write is acknowledged to anyone, every read that starts afterwards, on any replica, returns that value or a newer one. Maurice Herlihy and Jeannette Wing defined it in 1990; it is the C in CAP.
It is not serializability, which is about multi-object transactions matching some serial order that need not respect real time. Linearizability covers single operations on one object and must respect real time; a database offering both is called strictly serializable.
Eventual consistency promises convergence and nothing else. In Werner Vogels’s words, if no new updates are made to an object, eventually all reads return the last updated value. Note what that leaves out:
- No deadline. “Eventually” might be 50 ms or 20 minutes; only measurement tells you which.
- No promise about reads meanwhile. Any older value can come back, so two reads in a row can go backwards.
- No promise across objects. A two-key read can mix versions that never coexisted. In Doug Terry’s baseball example, a game whose real score is 2–5 can be read as any of 18 scores (3 visitor values × 6 home values), including a visitors’ lead that never happened.
Formally it is a liveness property (something good eventually happens), not a safety property (nothing bad ever happens), so no single read can ever violate it.
Strong needs coordination, which costs round trips on every operation and, during a partition, any answer at all on the minority side. The CAP and PACELC article covers that proof and the quorum arithmetic; this one maps the ground in between.
The spectrum in between
Researchers have defined dozens of consistency models. Seven come up in system design.
- Sequential. Everyone sees one order of writes that respects each client’s own order but may trail real time (Leslie Lamport, 1979). Reads can be stale, but never older than what you have already seen.
- Causal. If one write could have influenced another, everyone sees them in that order; unrelated writes may appear in different orders.
- Session guarantees. Read-your-writes, monotonic reads, monotonic writes and writes-follow-reads, scoped to one client: consistent with your actions, not everyone’s.
- Consistent prefix. A state that really existed, with no gaps: 8 possible baseball scores instead of 18.
- Bounded staleness. At most K versions or T seconds behind: a dial from near-strong (tiny T) to near-eventual (huge T).
A map, not a ruler
The levels form a partial order. Linearizable implies sequential, which implies causal, which implies all four session guarantees. But the session guarantees don’t rank against one another, and bounded staleness limits what every reader sees rather than one client’s view, which is why Cosmos DB ranks it above session. Jepsen’s consistency map draws the full hierarchy.
In a partition, linearizable and sequential models cannot serve the minority side. Causal and read-your-writes are sticky available: a client carries on if it stays on a replica that has its history. Monotonic reads, monotonic writes and writes-follow-reads can be totally available.
| Level | A read returns | Typical mechanism | Cost | In a partition | Good for |
|---|---|---|---|---|---|
| Linearizable | The latest acknowledged write | Consensus; leased leader | Majority round trip per write; 2× read price | Minority side errors | Ledgers, last unit, usernames |
| Sequential | One agreed order, possibly behind | One ordered log | Ordered writes; local reads | Minority side can’t write | Replicated config |
| Causal | Causes before effects | Version vectors, causal tokens | Metadata per write; some reads wait | Sticky available | Comments, chat |
| Read-your-writes | At least your own writes | Leader window, LSN token | Occasional wait or leader read | Sticky available | Profile, cart |
| Monotonic reads | Never older than your last read | Sticky replica, “seen version” token | Almost free | Totally available | Feeds, inboxes |
| Consistent prefix | A state that really existed | In-order replication, snapshots | Same as eventual | Any replica | Status timelines, scores |
| Bounded staleness | At most K versions or T seconds old | Track lag; throttle writes | 2× reads; throttled writes | Writes stall at the bound | Global read-mostly data |
| Eventual | Anything, converging later | Async replication, repair | Lowest | Totally available | Likes, catalogue, search |
Session guarantees: consistent with your own actions
Doug Terry and colleagues at Xerox PARC defined the four session guarantees in 1994 while building Bayou, a replicated store for often-offline mobile users. Their motivating bug is timeless: in the earlier Grapevine system, a user could change their password and then be told the new one was invalid, because the login server hadn’t received it yet.
A session is the sequence of reads and writes one application makes. The guarantees make that sequence look as if it came from one server, without making the whole system strong.
| Guarantee | Promise | Prevents | Client records after | Checked before |
|---|---|---|---|---|
| Read your writes | Your reads include your earlier writes | A saved edit “disappears” on refresh | Each write | Each read |
| Monotonic reads | Each read is at least as new as your last | A comment count goes 12 → 10 → 12 | Each read | Each read |
| Monotonic writes | Your writes apply in the order you made them | Home-then-Office address change ends as Home | Each write | Each write |
| Writes follow reads | Your write is ordered after what you had read | A reply stored before the post it answers | Each read | Each write |
The paper’s implementation is small. The client remembers the IDs of the writes it made and of the writes its reads depended on, and before each operation picks a server that has seen them (or reports that it can’t). Version vectors keep this compact, and every modern “session token” descends from it.
Read your writes
This is the one users notice. A user saves a new bio, the reload hits a lagging replica, the old bio comes back, and they save again. Lag is usually well under a second, so the bug is intermittent and escapes tests.
Monotonic reads
Without it, your reads can go backwards as a load balancer spreads them over replicas with different lag: a comment count shows 12, then 10, then 12, or a delivery status slips from “Out for delivery” back to “Preparing”.
Monotonic writes and writes follow reads
These two order writes, so they change what other people see too. Monotonic writes: change your delivery address to Home, then Office, and every replica applies them in that order. Writes follow reads: a reply is ordered after the post it answers on every replica. All four together get you most of the way to causal.
Causal consistency: causes before effects
Causal consistency stretches the session idea across users. If write B could have been influenced by write A (the same client wrote A first, or B’s author had read A), nobody may see B without A. Writes with no such link are concurrent, and replicas may show them in different orders.
To prevent it, each write carries its dependencies: a version vector, a “depends on write X” list, or one causal token covering everything the writer had seen. A replica exposes a write only after its dependencies. That costs metadata per write and an occasional short wait, but no cross-region round trip. Jepsen notes that a close variant, real-time causal, is proven to be the strongest model an always-available, convergent system can offer; in practice clients must also stick to a replica that has their history.
MongoDB’s causally consistent sessions, with majority read and write concern, provide exactly Terry’s four guarantees. Each operation returns a cluster time that the session carries forward, and a client can hand it to another session to continue the causal chain.
Who is reading? The baseball test
Terry, later at Microsoft Research, asked a sharper question: not “what does this data need?” but “what does each reader need?”. His example (a 2011 report, published in Communications of the ACM in 2013) is a baseball score stored as two numbers in a replicated key-value store, written by one scorekeeper and read by many.
| Reader | Reads the score to… | Guarantee Terry assigns | Why that is enough |
|---|---|---|---|
| Official scorekeeper | Add a run | Read my writes | He is the only writer, so his writes are all the writes |
| Umpire | Decide whether the home team must bat in the ninth | Strong | He reads someone else’s writes and needs the score now |
| Radio reporter | Announce the score every 30 minutes | Consistent prefix + monotonic reads | Old is fine; invented or backwards scores are not |
| Sportswriter | Write the report after dinner | Bounded staleness of 1 hour | The game ended an hour ago, so the score is final |
| Statistician | Add today’s runs to the season total | Strong for today; read my writes for the season total | She alone writes the season total |
| Fan checking stats | Talk about the season | Eventual | Stats change once a day |
Three lessons carry over to any design:
- Consistency belongs to the read, not the table. The same two numbers need four different guarantees. Only-eventual reads fail all but one of these readers; only-strong reads overcharge all but two.
- Knowing who writes buys strong results cheaply. A strong read must assume someone anywhere just wrote; read-your-writes only needs a server that has seen your writes, which minutes later is almost every server.
- Combine guarantees. The reporter needs consistent prefix (no invented scores) and monotonic reads (no score going backwards).
The same holds for a live cricket score: the scorer needs read-your-writes, the TV ticker consistent prefix plus monotonic reads, and the next morning’s report bounded staleness.
Where real products sit
Azure Cosmos DB: five named levels
Cosmos DB is the clearest product version of the spectrum: five levels, an account default you can weaken per request, and documented costs.
| Level | Promise | Reads from | A write commits to | Read cost |
|---|---|---|---|---|
| Strong | Linearizable | 2 of 4 local replicas | Every region | 2× |
| Bounded staleness | Behind by at most K versions or T time | 2 of 4 local replicas | Local majority (3 of 4) | 2× |
| Session | Read your writes and writes follow reads, via a token | 1 replica that has the token | Local majority | 1× |
| Consistent prefix | A transactional batch is seen whole and in order; single writes as eventual | 1 replica | Local majority | 1× |
| Eventual | Converges; reads may go backwards | Any 1 replica | Local majority | 1× |
- Strong and bounded-staleness reads cost double, because they read two replicas.
- Strong writes across regions pay for geography. Reads at every level, and writes at every level except multi-region strong, are documented at under 10 ms p99. Multi-region strong writes take about 2 × the round trip between the two farthest regions + 10 ms at p99. For Mumbai and Singapore at an illustrative 60 ms round trip, that is 2 × 60 + 10 = 130 ms. Strong is blocked by default beyond 8,000 km and isn’t offered with multiple write regions.
- Bounded staleness throttles writes when a region falls behind the bound, which across regions can be no tighter than 100,000 writes or 300 seconds.
Session, documented as the most widely used level, works as Terry described: each write returns a token, later reads send it, and a replica that hasn’t reached it is skipped. A client with no token for a partition gets eventual reads there until it writes.
Amazon DynamoDB: two kinds of read
Reads are eventual by default; set ConsistentRead to true for a strong read of a table or local secondary index. For items up to 4 KB, one read capacity unit buys one strong read per second or two eventual ones, so 20,000 reads/s of 3 KB items needs 20,000 RCUs strong or 10,000 eventual. Global secondary indexes and streams are always eventual.
Amazon S3: strong became the default
For its first 14 years S3 was eventually consistent: just after a PUT, a GET or LIST might miss the change. Since December 2020 every GET, PUT and LIST is strongly consistent, at no extra cost and, AWS says, no performance impact. Inside one region, good engineering can make strong cheap; distance is what stays expensive.
PostgreSQL and MySQL read replicas
The eventual system you most likely run is a leader with read replicas. PostgreSQL’s docs say it plainly: standby data is eventually consistent with the primary, typically under a second behind when the standby keeps up. synchronous_commit = remote_apply makes commits wait until synchronous standbys have applied them, trading write latency for fresh replica reads. MySQL’s WAIT_FOR_EXECUTED_GTID_SET() blocks on a replica until given transactions are applied.
Buying session guarantees cheaply
Most user complaints about eventual consistency are read-your-writes or monotonic-reads violations, and you can fix them without making the database strong.
Read from the leader for a while after a write. Record the user’s last write time; for the next N seconds, well above p99 replica lag, send their reads to the leader. With N = 10 s, 40,000 reads/s and 500 writes/s, if each writer makes about 4 reads in that window, the leader absorbs 500 × 4 = 2,000 reads/s, 5% of read traffic. The catch: when lag exceeds N, the guarantee quietly breaks.
Pin a session to one replica, for example by hashing the user ID. A replica never moves backwards, so this gives monotonic reads almost free, and read-your-writes too if the user also writes through that replica, as in leaderless stores (Vogels’s “stickiness”). If the replica fails, the session lands elsewhere, perhaps further behind.
Carry a version token. Return the write’s log position (a PostgreSQL LSN, MySQL GTID set, Cosmos DB session token or MongoDB cluster time). Later reads send it; a replica answers only if it has applied that position, otherwise it waits briefly or forwards to the leader. Keep the token in a cookie and any stateless app server can enforce it:
# After a write on the primary: remember how far the log had got.
session["min_lsn"] = primary.scalar("SELECT pg_current_wal_lsn()")
# On a read: use a replica only if it has replayed at least that far.
def pick_db(session):
replica = replicas.choose()
lsn = session.get("min_lsn")
if lsn is None:
return replica
caught_up = replica.scalar(
"SELECT pg_last_wal_replay_lsn() >= %s::pg_lsn", [lsn])
return replica if caught_up else primary
pg_current_wal_lsn() is the primary’s current write position, at or past your commit, so the check errs on the safe side.
Forward tokens between services. When one service writes and then calls another, pass the token along, or the second service may read a replica that hasn’t seen the write. Vogels describes a client-side variant too: drop any read older than the last version you showed.
Where the cheap tricks stop working
Session guarantees are per session, not per person: edit your profile on the phone and the laptop has no token. Tokens are often per partition, so a page reading ten partitions needs ten. A cache or CDN in front ignores all of this unless keyed or invalidated by version. And if an asynchronous leader dies before your write reaches a replica, the new leader never had it: your token points to a future that won’t arrive, so time out and choose between an error and a stale read.
What each choice costs
Strong consistency costs:
- Latency. Every write waits for a majority, and every strong read must reach a certainly-current replica: a millisecond or two in one data centre, but roughly 10 ms per 1,000 km of fibre across regions (130 ms in the Cosmos DB example).
- Availability. During a partition the minority side waits or errors (CAP); during a leader election, writes pause.
- Throughput. Strong reads cost 2× in DynamoDB and Cosmos DB, and leader-only reads can’t scale out with replicas.
Eventual consistency costs:
- Visible anomalies. Vanishing edits, wobbling counts and out-of-order replies become duplicate submissions and tickets.
- Conflict resolution. If several replicas accept writes, concurrent updates must be merged (last-write-wins, version vectors, CRDTs).
- Application complexity. Invariants the database no longer enforces (no double booking, no negative balance) move into your code as idempotency keys and commit-time re-checks.
- Harder testing. The bugs appear only under lag, so you need lag injection in staging and Jepsen-style fault tests.
Choosing per feature
For each read path, ask: what happens if this read is stale or out of order? Then take the weakest level that makes the answer “nothing bad”.
| Feature | If a read is stale or reordered… | Level | How |
|---|---|---|---|
| UPI-style wallet balance and ledger | Money is created or spent twice | Linearizable | Leader or consensus store; conditional debit in one transaction |
| Last seat in a tatkal-style rush | Two passengers, one berth | Linearizable | Compare-and-set on the seat (double booking) |
| Username uniqueness | Two accounts, one name | Linearizable | Unique constraint or consensus-backed CAS |
| Profile edit, settings, cart | Your own change “disappears” | Read-your-writes | Leader window or LSN token |
| Chat, threaded comments | A reply shows before its question | Causal | Causal session or per-conversation order |
| Order or delivery status | Status jumps backwards | Monotonic reads + consistent prefix | Sticky replica, versioned updates |
| Likes, view counts | Off by a few for seconds | Eventual | Any replica, cached counters |
| Feed, product catalogue | A post or price shows a little late | Eventual | Replicas, caches, CDN |
| Search index | A new listing is searchable seconds later | Eventual | Async indexing via CDC |
Notice the pattern in sale and booking flows: browsing is eventual, the final step is strong. The catalogue shows “3 left” from a replica; a compare-and-set on the stock row decides who gets one.
In the interview
Name the reader, the anomaly and the level in one breath: “If the profile page reads a lagging replica, the user’s edit vanishes, so I need read-your-writes there, via an LSN token, not a strong database.”
Going deeper: convergence, caches and lag
How replicas actually converge
“Eventually” is the work of background machinery. Hinted handoff keeps writes for a down replica and replays them later, best effort. Read repair writes the newest version back to stale replicas a read happened to touch. Anti-entropy repair compares Merkle trees (hashes over key ranges) and streams only the ranges that differ; Cassandra leaves it to the operator because it is heavy. Replicas must also settle concurrent writes identically, by last-write-wins, version vectors or CRDTs: see clocks, ordering and conflict resolution.
Your consistency level ends at the database
A strongly consistent database does not make a strongly consistent product. A cache, a CDN, a search index fed by CDC and a read model are all eventual copies with their own lag. Decide the level per read path, end to end, and version or invalidate caches on paths that need read-your-writes (see cache invalidation). Even leader reads aren’t automatically linearizable: a deposed leader that hasn’t noticed can serve an old value, which is why consensus systems confirm leadership or hold a lease first.
“Eventual” has no deadline, so measure it
Vogels’s rule of thumb: if the inconsistency window is shorter than the time a user takes to load the next page, most users never notice, until lag spikes during a bulk import or replica restart. Alert on replication lag, not just errors.
Cheat sheet
| Term | Remember it as |
|---|---|
| Linearizable (strong) | Acts like one copy; reads that start after an acknowledged write see it |
| Sequential | One agreed order, may lag real time |
| Causal | Causes before effects for every reader |
| Session guarantees | Read your writes, monotonic reads, monotonic writes, writes follow reads |
| Consistent prefix | A state that really existed; never a mash-up |
| Bounded staleness | At most K versions or T seconds behind |
| Eventual | Converges if writes stop; no deadline, no promise meanwhile |
| Cheapest fixes | Leader window, sticky replica, LSN or session token |
| Price of strong | Majority round trips, 2× read cost, minority side unavailable |
How to say it in the interview
“Strong usually means linearizable: once a write is acknowledged, every later read sees it. Eventual only promises convergence, so meanwhile users can see their edits vanish, counts go backwards and replies before questions. I choose per read path. The ledger and seat holds are strong, with compare-and-set on a leader or consensus store. Profile and cart need read-your-writes, via an LSN token. Comments are causal. Likes, the feed and search are eventual, and I’d alert on replica lag to know how eventual they really are.”
Isn’t eventual consistency just strong consistency with a small delay? No. The definition sets no bound on the delay, and meanwhile reads can go backwards or mix values that never coexisted. A short delay is typical, not promised.
How do you give read-your-writes when reads go to async replicas behind a load balancer? Return a version token (an LSN or GTID) with each write and send it with later reads; a replica answers only once it has applied that position, otherwise it waits briefly or forwards to the leader. Simpler: send a user’s reads to the leader for a few seconds after they write.
What is the difference between linearizability and serializability? Linearizability covers single operations on one object and respects real time. Serializability covers multi-object transactions matching some serial order, not necessarily the real-time one. Strict serializability is both.
A user edits their profile on the phone, then sees the old one on the laptop. Does session consistency help? Not by itself: the token lives in the phone’s client. Store the last-write position per user on the server, or route that user’s reads to the leader briefly after any write, from any device.
Where to go next
- CAP theorem and PACELC: why strong costs availability and latency.
- Data replication: where replica lag comes from.
- Quorums and leaderless replication: tunable consistency, read repair and sloppy quorums.
- Clocks, ordering and conflict resolution: version vectors and CRDTs.
- Consensus and leader election: how Raft gives linearizable reads and writes.
- ACID vs BASE and serializable isolation: the same trade-off seen through transactions.
- Contention and double booking: protecting the last seat.
References and further reading
- Session Guarantees for Weakly Consistent Replicated Data (Terry et al., PDIS 1994)cs.utexas.eduDefines read your writes, monotonic reads, writes follow reads and monotonic writes, the Grapevine password bug, and the read-set/write-set implementation.
- Replicated Data Consistency Explained Through Baseball (Terry, Communications of the ACM, 2013)cacm.acm.orgSix read guarantees, the 18-versus-8 possible scores, and which guarantee each reader of a baseball score needs.
- Eventually Consistent (Vogels, ACM Queue, 2008)allthingsdistributed.comThe definition of eventual consistency, the inconsistency window, session and monotonic variants, and client stickiness and versions as implementation tricks.
- Jepsen: Consistency modelsjepsen.ioPrecise definitions of linearizable, sequential, causal, PRAM and session models, and which can be totally or sticky available.
- Azure Cosmos DB: Consistency level choiceslearn.microsoft.comThe five levels, session tokens, quorum reads and writes per level, the 2× read cost, the strong-write latency formula and bounded-staleness limits.
- Amazon DynamoDB Developer Guide: Read consistencydocs.aws.amazon.comEventually versus strongly consistent reads, eventual reads at half the cost, and always-eventual GSIs and streams.
- Amazon S3 now delivers strong read-after-write consistency (AWS, December 2020)aws.amazon.comS3 moved from eventual to strong read-after-write and list consistency at no extra cost and with no performance change.
- PostgreSQL documentation: Hot Standbypostgresql.orgThe docs’ own words: data on a hot standby is “eventually consistent” with the primary, so the same query on both can differ.
- MongoDB manual: Causal consistency and read and write concernsmongodb.comCausally consistent sessions give all four session guarantees with majority read and write concern; cluster time is carried by the session.
- Apache Cassandra documentation: Repaircassandra.apache.orgHow replicas converge: best-effort hints, and operator-run anti-entropy repair that compares Merkle trees and streams the differences.