SevyDevy

Guide · System design

Consistency Models: From Linearizable to Eventual

26 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • Consistency models are the promises a system with several copies of its data makes about what a read may return. Each extra copy, kept on another machine, is a replica. The models run from linearizable, the strictest, to eventual, the loosest.
  • Linearizable means the system acts as if there were one copy: once a write finishes, every read that starts later sees it. It is the easiest to reason about and the costliest to serve.
  • Sequential and causal consistency drop the "starts later" rule, so a reader may run behind. Sequential still gives every reader one shared order. Causal only keeps cause before effect, such as a post before its reply.
  • Session guarantees protect one user's own view. Read-your-writes means you always see your own changes, and monotonic reads means your view never goes backwards. Eventual consistency only promises that copies agree once writes stop.
  • Beginners can stop after the section on choosing a model, which ends with one table. Engineers get a runnable program that stops one user's view going backwards, PostgreSQL 16 commands that catch a stale replica, and what MongoDB, DynamoDB and ZooKeeper promise exactly.

What goes wrong when an app reads from copies of its data?

Readers start seeing things that look impossible: a score that goes backwards, a comment that vanishes, a reply that shows up before the post it answers. No data is lost. Each read simply came from a copy that was further behind.

Picture a common setup. A live cricket scores app keeps one main database, called the primary or leader, which takes every write. Four read replicas sit behind it and serve the reads, each copying changes from the primary a moment later. The setup is made up, but the problems are real.

During a T20 final, 18 lakh (1.8 million) fans have the app open. The next morning the team sorts the complaints into four kinds.

  • The score went from 149/3 back to 145/3 on refresh, then jumped to 149/3 again.
  • A fan posted a comment, refreshed, and the comment vanished for ten seconds.
  • A reply saying a catch was off a no-ball showed up before the comment it was answering.
  • Three fans entered the next-ball prediction game after the ball was bowled, and won points they should never have got.

Each complaint is a different broken promise. Sending every read to the primary would fix all four, and would also make the primary the slowest part of the app on match nights.

The better question is what each feature actually needs. Consistency models answer that question. This guide walks the range from strictest to loosest, with one feature of the app and one real system at each step.

What are consistency models?

Consistency models are the rules a distributed system promises about which values a read may return, given the writes that came before it. Stronger models allow fewer surprises, and cost more waiting or availability.

A distributed system is a group of computers that share data over a network and work as one service. Once data lives on more than one machine, the copies can disagree for a moment. A consistency model says how much of that disagreement a reader may notice.

Picture a single-screen cinema in north Kolkata on the opening day of a big film. Seat information lives in three places, and each one catches up at its own speed.

  • The seat chart at the booking window maps to linearizable. The moment a seat is sold it is crossed off, and anyone at the window sees the true picture.
  • The housefull board outside maps to sequential. The manager flips it a few minutes after the last seat sells. It is late, but it never flips back and forth.
  • Posters around town map to eventual. Some still show last week's film for days, but sooner or later every one is replaced.

Where the picture breaks: the cinema has one seat chart that everyone trusts. A database has no such master copy that every reader can see at once. It has to build that illusion out of messages between machines, and that is what costs time.

Here is the whole range as one line. Start at the top, the strictest promise, and read each arrow as one promise being dropped. The note on each box names the app feature that sits there.

The consistency range in the scores app

Each step down drops one promise and needs less coordination between copies. Notice that only the top box needs the leader on every read.

Diagram connections:

  • Linearizable to Sequential (Drop real-time order)
  • Sequential to Causal (Drop one shared order)
  • Causal to Session guarantees (Keep only each user's view)
  • Session guarantees to Eventual (Drop per-user promises)
  • Linearizable: Prediction cutoff: acts like one copy, in real time
  • Sequential: Ball-by-ball feed: one order for every fan
  • Causal: Comment wall: replies always after their posts
  • Session guarantees: Your own comments stay; your score never goes back
  • Eventual: Fans-watching counter: catches up later

For these five, a line is a fair picture. Jepsen, a project that tests databases, publishes a full map that branches. The session guarantees are four separate promises. Models for transactions, groups of reads and writes that run together, form another branch, and serializability is the best known. Eventual consistency is not on that map, because it only promises that copies converge.

What is linearizability, and why is it called strong consistency?

Linearizability means the system behaves as if there were only one copy of the data, and every operation takes effect at a single instant. Once a write finishes, every read that starts afterwards sees it.

The key word is afterwards, measured on a real clock. If write A finishes before read B starts, B must see A. This rule is called real-time order. If the two overlap in time, either order is allowed. Herlihy and Wing introduced linearizability in a 1990 paper.

Many product docs call this strong consistency, which is why the two names get mixed up. The FAQ at the end covers where they differ.

In the cinema, this is the seat chart at the booking window. When seat F12 is sold, the next person at the window can never buy F12 again.

In the scores app, the next-ball prediction game needs exactly this. Fans guess the result of the next ball, such as a dot, a four or a wicket. Guessing must stop the moment the bowler starts the run-up.

The bug happened because the prediction service checked the window on a replica that was 4 seconds behind (made up). It still said open, though the scorer had already closed the window on the primary. Three fans saw the six on television first.

The price is real. In a single-leader setup like this one, a linearizable read must reach the leader. The leader must also make sure it has not been replaced, usually by checking with a majority of copies. That adds network trips, and the leader becomes a busy spot on match nights. MongoDB offers this for single-document reads as its linearizable read concern; the engineer section covers its limits.

What is sequential consistency?

Sequential consistency means every reader sees all operations in one order that everyone agrees on, and each client's own operations stay in the order it made them. Unlike linearizability, that shared order may run behind the real clock.

So a reader can be stale, but never confused about order. Once it has seen an event, it never sees a state from before that event. Leslie Lamport defined the model in 1979, for multiprocessor computers.

In the cinema, this is the housefull board. Every passer-by sees the same sequence: seats available, few seats left, housefull. Someone may see it a few minutes late. Nobody sees housefull, then seats available, then housefull again.

In the scores app, the ball-by-ball feed fits here. Over 17 must read 17.1, 17.2, 17.3 for every fan, in that order. If one fan sees the wicket on 17.3 a second after another fan, nobody is hurt.

ZooKeeper, a coordination service many systems use for settings and leader election, works close to this. Its 2010 paper says all writes go through one global order, while each server answers reads from its own copy, which may be stale.

What is causal consistency?

Causal consistency means that if one event could have caused another, everyone sees them in that order. Events with no link between them may appear in different orders to different readers.

Two events are linked when someone saw the first before making the second, or made both, one after the other. A reply is linked to the post it answers. Two fans cheering the same six, without seeing each other, are not linked.

Jepsen's reference page explains it with a group chat. One person asks about lunch, and two friends reply yes and no. Readers may see the two replies in either order, but nobody sees a reply before the question.

The app's comment wall broke this rule. It runs on a separate store with three nodes, and any node accepts a write and copies it to the others. One fan praised a catch at long-on, and another fan replied that the ball was a no-ball.

Follow the arrows in the diagram below. The reply and the post take two different paths to node 3, and the reply's path is faster.

How a reply arrived before its post

Node 3 shows the reply first because nothing tells it the reply depends on the post. Copy times are (made up).

Diagram connections:

  • Fan posts: brilliant catch to Second fan reads the post
  • Second fan reads the post to Second fan replies: no-ball
  • Second fan replies: no-ball to Reply copied in 0.2 s
  • Fan posts: brilliant catch to Post copied in 3 s
  • Reply copied in 0.2 s to Third fan sees reply first (Lands first)
  • Post copied in 3 s to Third fan sees reply first (Lands later)
  • Fan posts: brilliant catch: First fan's phone → Node 1
  • Second fan reads the post: Node 1 → Second fan's phone
  • Second fan replies: no-ball: Second fan's phone → Node 2
  • Reply copied in 0.2 s: Node 2 → Node 3
  • Post copied in 3 s: Node 1 → Node 3
  • Third fan sees reply first: Node 3 → Third fan's phone

The fix is to carry the link along with the data. Each reply records the id of the post it depends on. Node 3 holds the reply back until that post has arrived, then shows both in the right order.

This tracks only the link the app cares about, the post being answered. Full causal consistency tracks everything the writer had seen. Systems do this with log positions or with vector clocks: a list of counters, one per copy, recording how many changes from each copy have been seen.

Causal consistency is popular because it survives network trouble better than the stricter models. Jepsen calls it sticky available: during a partition, a client can keep working as long as it stays with the same server. MongoDB's causally consistent sessions are one real version, covered in the engineer section.

What are read-your-writes and monotonic reads?

Read-your-writes and monotonic reads are session guarantees: promises about what one user sees, not what everyone sees. Read-your-writes means you always see your own changes. Monotonic reads means your view never moves backwards.

Werner Vogels described both in his 2008 essay on eventual consistency, next to causal, session and monotonic write consistency. He wrote that these two are the most desirable, because they make applications simpler to build.

Two of the four complaints were session bugs. The score went backwards because two refreshes hit two replicas with different lag. The comment vanished because the fan's next read went to a replica that had not received it yet.

Neither bug needs the leader for every read. Each phone only needs to remember how far along its last view was, and refuse any replica that is further behind. The engineer section turns that idea into a short program you can run. PostgreSQL exposes the log position this check needs, and that section shows it on a real replica.

Only one fan's view is protected. Two different fans may still see different scores for a second. For a live score that is fine, as long as each fan's own screen only moves forward.

What is eventual consistency?

Eventual consistency promises only that if writes stop, all copies will sooner or later return the same latest value. It says nothing about how long that takes, or what readers see in between.

Vogels calls that gap the inconsistency window: the time between an update and the moment every reader is sure to see it. His best-known example is DNS, the internet's name system, where a change spreads through caches that expire on a timer.

In the cinema, these are the posters around town. A poster for last week's film is wrong for a few days. Nobody makes a decision that matters from it, and every poster is replaced in the end.

In the scores app, the fans-watching counter and the like counts on comments sit here. If one fan sees 18,02,311 watching and another sees 18,02,290, nothing breaks. The counter shows the size of a crowd, not a fact anyone acts on.

This is the honest reason eventual consistency is everywhere. Any replica can answer any read without asking anyone else. So it is cheaper, faster, and keeps answering when parts of the network fail.

How do you choose between strong and eventual consistency?

Choose per feature, by asking what a stale or out-of-order read would cost. Use the strongest model only where a wrong read causes real damage, and the weakest one the feature can live with everywhere else.

The team walked every feature through four questions. Start at the top of the diagram below. The first yes decides the model.

Picking a model for one feature

Ask the questions in order. Notice that most features fall through to the cheaper models at the bottom.

Diagram connections:

  • Can a stale read do harm? to Linearizable (Yes)
  • Can a stale read do harm? to One order for all fans? (No)
  • One order for all fans? to Sequential (Yes)
  • One order for all fans? to Depends on earlier posts? (No)
  • Depends on earlier posts? to Causal (Yes)
  • Depends on earlier posts? to User checks own changes? (No)
  • User checks own changes? to Session guarantees (Yes)
  • User checks own changes? to Eventual (No)
  • Can a stale read do harm?: Money, points or fairness at stake
  • Linearizable: Prediction cutoff, read from the leader
  • One order for all fans?: Everyone must agree on the sequence
  • Sequential: Ball-by-ball feed with sequence numbers
  • Depends on earlier posts?: Replies, edits, follow-ups
  • Causal: Comment wall, replies wait for posts
  • User checks own changes?: Or watches a value that only rises
  • Session guarantees: Log position check per phone
  • Eventual: Fans-watching counter and likes

The table puts the whole plan in one place. Read across a row to see the promise, where the app sends those reads, and what happens during a network partition.

The scores app's consistency plan, one row per model (availability classes from Jepsen's model pages)
ModelPromise in plain wordsApp featureWhere reads goDuring a partition, in general
LinearizableActs like one copy; a finished write is seen by every later readNext-ball prediction cutoffLeader only, confirmed by a majoritySome or all nodes stop answering
SequentialEveryone sees one shared order, possibly lateBall-by-ball feedAny replica, applied in sequence orderSome or all nodes stop answering
CausalLinked events appear in cause-then-effect orderComment wall and repliesAny node that already has the postWorks if each client stays on one server
Session guaranteesYou see your own writes; your view never goes backOwn comments, live score screenAny replica at least as far along as the phoneMonotonic reads keep working anywhere; read-your-writes needs one server
EventualCopies agree once writes stopFans-watching counter, likesAny replicaAny node can keep answering

The last column is the general rule for each model. In this app's feed, a replica cut off from the leader can still show its fans an older, correctly ordered feed.

Only one feature, the prediction cutoff, needs the leader. On a match night that is under 1 percent of all reads (made up). Everything else stays on the cheaper replicas.

How do you build these guarantees on a primary with read replicas?

You build each promise by deciding which copy may answer a read, and what the reader must remember. The leader answers the strict reads, sequence numbers fix order, and a log position check gives each phone its session guarantees.

A leader read for the prediction cutoff

Here read_latest is a made-up helper for a linearizable read: it asks the primary, which confirms with a majority that it is still the leader. The window record holds the current ball number and whether guessing is open.

python
def submit_prediction(db, match_id, ball, fan_id, guess):
    # Linearizable read: ask the leader, never a replica
    window = db.read_latest(f"window:{match_id}")
    if window["ball"] != ball or not window["open"]:
        return "Too late, this ball is already live"
    db.write(f"pred:{match_id}:{ball}:{fan_id}", guess)
    return "Saved"

A plain read could hit any replica and see an old, open window. The check and the write are still two steps. If the scorer closes the window between them, one guess can land just after the close. Closing that gap needs both as one atomic step on the leader, such as a conditional write or a transaction that locks the window record.

Sequence numbers for the ball-by-ball feed

The feed has one writer, the scorer's app, so order is easy. It gives every ball the next number: 101, 102, 103. Phones apply events strictly in number order, and wait briefly if one is missing.

Doug Terry's paper on consistency through baseball calls the reader's side of this consistent prefix: you see the writes in order from the start, though maybe not the latest. With many writers, one place must hand out the numbers, such as the leader.

A log position check for session guarantees

In a single-leader setup, the primary writes every change to a log, and each replica applies that log in order. So a replica's position in the log says how far along it is. The phone remembers the furthest position it has been shown, and only reads from copies at least that far along.

The program below runs as it is on Python 3.13. It sets up a primary, a fresh replica and a stale replica that missed the 149/3 update, then reads the score three times through a round-robin load balancer.

python
class Copy:
    """One copy of the data: the primary or a replica."""
    def __init__(self):
        self.data = {}
        self.applied = 0  # how far along the change log this copy is

    def apply(self, position, key, value):
        self.data[key] = value
        self.applied = position


class SessionReader:
    """Monotonic reads and read-your-writes for one phone."""
    def __init__(self, replicas, primary):
        self.replicas = replicas
        self.primary = primary
        self.seen = 0  # furthest log position this phone has been shown
        self.turn = 0

    def read(self, key):
        self.turn += 1  # take replicas in turn, like a load balancer
        k = self.turn % len(self.replicas)
        order = self.replicas[k:] + self.replicas[:k] + [self.primary]
        for copy in order:
            if copy.applied >= self.seen:  # never older than this phone's view
                self.seen = copy.applied
                return copy.data.get(key)

    def after_write(self, position):
        self.seen = max(self.seen, position)  # read your own writes


primary, fresh, stale = Copy(), Copy(), Copy()
for copy in (primary, fresh, stale):
    copy.apply(1, "score", "145/3")
for copy in (primary, fresh):
    copy.apply(2, "score", "149/3")  # the stale replica missed this

print("plain reads:  ", fresh.data["score"], stale.data["score"])
phone = SessionReader([fresh, stale], primary)
print("session reads:", phone.read("score"), phone.read("score"), phone.read("score"))

primary.apply(3, "comment:77", "What a catch!")  # the fan posts
phone.after_write(3)
print("own comment:  ", phone.read("comment:77"))

Pause and predict: the stale replica answers the first session read. What do the second and third reads print? Running it prints three lines:

text
plain reads:   149/3 145/3
session reads: 145/3 149/3 149/3
own comment:   What a catch!

What just happened? Plain reads went backwards, from 149/3 to 145/3. The session reader showed 145/3 first, because nothing newer had been seen yet. After it saw 149/3, it skipped the stale replica. The fan's own comment came from the primary, because no replica had it.

How can you see a stale replica and its log position in PostgreSQL?

PostgreSQL shows the log position directly: the primary reports where its write-ahead log ends, and each replica reports how far it has replayed. The write-ahead log, or WAL, is the change log every replica applies. Positions are called LSNs, log sequence numbers.

Streaming replication is asynchronous by default, so a replica read can be stale. The docs say the delay is typically under one second when the replica keeps up. These steps ran on PostgreSQL 16.15 with one local replica, as of 19 October 2026.

  1. Confirm the replica is asynchronous

    On the primary run SELECT sync_state FROM pg_stat_replication; and expect async. That is the default when synchronous_standby_names is empty.

  2. Create lag on purpose

    On a test replica only, as a superuser, run SELECT pg_wal_replay_pause(); so it stops applying changes. Check that SELECT pg_get_wal_replay_pause_state(); returns paused, then update the score row on the primary.

  3. Read the same row twice

    The primary returns 149/3 and the replica still returns 145/3. That is the backwards score from the opening, on a real database.

  4. Note the primary's position

    Right after the write, run SELECT pg_current_wal_lsn(); on the primary. Our run printed 0/3026130; your number will differ.

  5. Ask the replica if it caught up

    Run SELECT pg_last_wal_replay_lsn() >= '0/3026130'::pg_lsn; on the replica. It printed f while paused, and pg_wal_lsn_diff between the two positions gave 112 bytes.

  6. Resume and check again

    Run SELECT pg_wal_replay_resume(); then repeat the check. It printed t, and the replica returned 149/3.

That comparison is the session reader's check, done by the database. With the default synchronous_commit setting, save the primary's LSN after each write in the user's session. Send a read to a replica only when its replay LSN has reached it.

If you would rather make the primary wait, set synchronous_commit to remote_apply with a synchronous standby. Each commit then waits until that standby has replayed it. In our run a commit hung while replay was paused, and was already committed on the primary when the client gave up after 5 seconds.

What do MongoDB, DynamoDB and ZooKeeper actually promise?

Each one offers some of these models as an option, with exact limits that are easy to miss. The limits below are from each product's current docs, as of 19 October 2026.

MongoDB

A read concern is MongoDB's setting for how fresh and durable a read must be. Its linearizable read concern works only on the primary. Its guarantee only applies when the query filter uniquely identifies a single document. Search on an immutable field such as _id, or a unique index; otherwise a concurrent update to the search key can make it miss the document. It returns every write that a majority of copies confirmed before the read began.

The manual warns these reads may be significantly slower than majority or local reads. It also says to always set maxTimeMS, a time limit on the query, so a read errors instead of blocking when a majority is unreachable.

MongoDB's causally consistent sessions give read-your-writes, monotonic reads, monotonic writes and writes-follow-reads. That holds only for reads with majority read concern and writes with majority write concern, and one thread at a time per session.

DynamoDB

DynamoDB reads are eventually consistent by default. Setting ConsistentRead to true returns data reflecting all earlier successful writes, and costs twice as much. Strongly consistent reads are not supported on global secondary indexes, a kind of extra index, or on streams.

The read consistency page does not use the word linearizable for these reads. For global tables, the default multi-Region mode usually copies changes to other Regions within a second, a typical figure, not a promise. A multi-Region strong consistency mode instead copies each change to another Region before the write returns.

ZooKeeper

ZooKeeper's guarantee list includes what it calls sequential consistency, meaning updates from a client are applied in the order sent. It also promises that a client never sees an older view, even after failing over to another server.

Its timeliness promise is loose: a client's view is up to date within a bound of tens of seconds. A client that must see the newest value calls sync before reading, which makes its server apply pending writes first.

How do you test what consistency a system really gives?

You test it by logging every read and write with its start and end time, then checking whether any allowed order explains what readers saw. One impossible result, such as a score going backwards, proves a promise was broken.

Normal tests miss these bugs because a quiet test server never lags. The team built a match-night rehearsal that creates lag on purpose.

  1. Replay a real match

    A script replays a recorded T20 match at full speed: about 240 balls, 9,000 comments and 50,000 simulated phones (made up).

  2. Log every operation

    Each read and write is saved with its start time, end time, key, value and log position.

  3. Break things on purpose

    Midway, pause replay on one replica for 5 seconds and restart another. Those are the conditions of a real final night.

  4. Check each promise

    A checker looks for a phone whose log position went down, a reply shown before its post, and a guess accepted after its window closed.

  5. Block the release on any hit

    One violation is enough to stop the release. Attach the exact operations so an engineer can replay them.

For linearizability itself, use a ready checker rather than your own. Jepsen's Knossos checks whether a recorded history is linearizable. Its sibling Elle does the same kind of check for histories of multi-item transactions.

How do you explain consistency models in a system design interview?

Explain them as a range of promises, then place each feature of the design on that range with a reason. Show that you know what each step costs.

  1. Ask what a stale read breaks

    Before choosing anything, ask what goes wrong if a user sees old data for a few seconds. The answer differs per feature.

  2. Draw the range

    Sketch the line from linearizable to eventual, and say in one phrase what each step drops.

  3. Place each feature

    Put money, points or locks at the strong end. Put feeds in the middle, and counters at the loose end.

  4. Name the mechanism

    Leader reads for linearizable, sequence numbers for order, dependency ids for causal, log position checks for sessions, any replica for eventual.

  5. State the price

    Stronger models add network trips, and the strongest stop answering on some or all nodes during a partition. Say where that cost lands in your design.

  6. Say how you would test it

    Mention logging operations under injected lag, and checking for impossible results like a value going backwards.

If the interviewer asks about failures, connect this to replication and to the CAP and PACELC trade-offs. Those ideas explain why a stronger promise means more waiting, or refusing to answer during a split.

Questions people ask

Is strong consistency the same as linearizability?
Often, but not always. Many people use strong consistency to mean linearizability, where every read sees the latest finished write. Some product documentation uses the phrase for its own, narrower promise. DynamoDB's strongly consistent reads, for example, are documented without the word linearizable and do not work on global secondary indexes. Read the exact promise before you rely on it.
What is the difference between linearizability and serializability?
Linearizability is about single operations on single items, in real-time order. Serializability is about transactions that touch many items: the result must match some order where transactions ran one at a time. Serializability alone does not promise that order matches the real clock. The model that adds real-time order to serializability is called strict serializability.
What does consistency mean in the CAP theorem?
In the CAP theorem, consistency means linearizability, the strongest model in this guide. The Gilbert and Lynch proof calls it atomic consistency. CAP says that during a network partition, a system must choose between staying linearizable and answering every request. It is different from the consistency in ACID transactions, which means the data obeys your own rules.
Is eventual consistency bad?
No. Eventual consistency is the right choice for data where a few seconds of staleness hurts nobody, such as view counts, likes and search indexes. It is cheaper and keeps answering when parts of the network fail. It becomes a problem only when a feature quietly needed a stronger promise.
Can one app use different consistency models?
Yes, and this is the normal design. Many databases let you choose per request. DynamoDB has a ConsistentRead flag, and MongoDB lets you set a read concern per operation. The scores app in this guide uses five models across its features, and only one of them needs the leader.
Does read-your-writes consistency need every read to go to the primary?
No. The client remembers the log position of its last write, and any replica at least that far along may serve the read. In PostgreSQL that position is the LSN from pg_current_wal_lsn. If no replica has caught up, the read falls back to the primary. Most reads still go to replicas.
How long does eventual consistency take to catch up?
There is no fixed time. Eventual consistency only promises that copies agree once writes stop. Without failures, the delay depends on network speed, load and the number of copies. PostgreSQL's docs say asynchronous streaming replicas are typically under one second behind when they keep up. DynamoDB says the same for global tables in the default mode. Both are typical figures, not promises.

Sources and further reading

Practise this with Lala

Practise