Engineering Atlas › System Design Fundamentals

Strong, eventual and causal consistency

13 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

Linearizability

Every operation appears to take effect at a single instant between its start and its end, and all clients agree on that order. If write A finishes before read B starts, B must see A or something newer. This is the "behaves like one copy" model. It needs a leader with a valid lease, or a quorum round trip, per operation. Under a partition, the minority side cannot serve linearizable reads.

Sequential consistency

All clients see the same total order of operations, and each client's own operations appear in program order. Unlike linearizability, the order need not match real time. A read can return a value that is a few seconds stale, as long as everyone agrees on the sequence. ZooKeeper offers this for reads from followers, combined with linearizable writes: a client never sees an older view even after failing over to another server, its view is promised fresh only within a time bound of the order of tens of seconds, and a client that needs the latest value calls sync first (ZooKeeper guarantees).

Causal consistency

If operation A could have influenced B (same client did A then B, or B read A's result), every replica shows A before B. Unrelated operations may appear in different orders on different replicas. Causal consistency can stay available during a partition, but only sticky available: each client must keep talking to the same replica, because a client that fails over to a replica missing its earlier writes would see time run backwards. A slightly stronger variant, real-time causal, is the strongest model known for an always-available system (Jepsen). That makes causal attractive for geo-replicated systems that route each user to one region. It is implemented by tracking dependencies with vector clocks or dependency lists.

Session guarantees

These are client-centric promises that are cheap and cover most user complaints. Read-your-writes: after you write, your own reads see it. Monotonic reads: you never see older data after seeing newer data. Monotonic writes and writes-follow-reads keep a session's writes ordered. A common implementation is a session token holding the last-seen log position; replicas that are behind either wait or forward the read. Cosmos DB's session tokens work this way, and they are per partition: a client that never wrote to a partition, or was just restarted and lost its token cache, gets eventual consistency on reads there (Cosmos DB docs).

Eventual consistency

If writes stop, all replicas converge to the same value eventually. It says nothing about what you read meanwhile and nothing about how conflicts resolve, so it must be paired with a merge rule. Most systems also say how stale reads can get, for example a replication lag p99 of 200 ms.

Isolation is a separate axis

Consistency models describe single-object operations across replicas. Transaction isolation (serializable, snapshot, read committed) describes multi-object transactions. Strict serializability combines both: serializable transactions in real-time order, as Spanner provides.

Down the stack

Layer 1The contract

Real products expose the ladder as switches, and the switch names tell you the contract. DynamoDB reads are eventually consistent unless the request sets ConsistentRead=true, which costs twice the read capacity: a strongly consistent read of up to 4 KB takes one read unit, an eventually consistent one half, a transactional one two (DynamoDB docs). The switch is also narrower than its name on global tables: in the default multi-Region eventual mode, a "strongly consistent" read only sees the latest write if that write was made in the same Region; reading the latest version anywhere needs a table created in multi-Region strong consistency mode (global tables). etcd serves linearizable reads by default and offers serializable reads, which any member answers from local state and which may be stale. MongoDB combines readConcern (local, majority, linearizable) with writeConcern and causally consistent sessions. Current versions default to w: "majority" writes in most replica sets but still read at local, which can return data that is later rolled back (MongoDB docs), and causal sessions only keep their promises when both read and write concerns are majority (Jepsen). Cosmos DB offers five named levels: strong, bounded staleness, session, consistent prefix and eventual. Strong and bounded-staleness reads touch two replicas and so cost twice the request units, bounded staleness in a multi-region account cannot be set tighter than 100,000 writes or 300 seconds, and strong consistency across regions more than 5,000 miles apart is blocked by default because of write latency (Cosmos DB consistency levels). Cassandra lets you pick replica counts per request: with N=3, writing at QUORUM and reading at QUORUM means every read set overlaps every write set, but concurrent operations are still not linearizable unless you use lightweight transactions, which run Paxos and cost several extra round trips.

A testable statement of a model is a rule over a history of (invoke, complete) events. Jepsen's checkers, Knossos and Elle, take recorded histories and search for a legal order, which is exactly the Practice problem below.

Layer 2The mechanism

Linearizable reads in Raft use ReadIndex: the leader records its current commit index, confirms it is still leader with one heartbeat round to a majority, waits until its state machine has applied up to that index, then answers. Lease reads skip the heartbeat round by trusting that no new leader can exist before the lease ends, which is safe only if clock drift is bounded.

Session guarantees ride on a log position. In PostgreSQL the primary's pg_current_wal_lsn() returns a 64-bit byte offset into the WAL; a replica compares it against pg_last_wal_replay_lsn() and waits until it catches up. Applications have had to poll for that; PostgreSQL 19, in pre-release as of late 2026, adds a WAIT FOR LSN command with a timeout that blocks on the standby until the position is replayed (PostgreSQL 19 docs). MongoDB returns an operationTime and the client sends it back as afterClusterTime.

Causal systems track dependencies explicitly, with vector clocks or, as in the COPS research system, a list of (key, version) dependencies checked before a write becomes visible. Eventual systems in the Dynamo family attach a write timestamp to each value (Cassandra stores a microsecond timestamp per cell) and resolve conflicts by last-writer-wins, with read repair and Merkle-tree anti-entropy for convergence.

Spanner gets strict serializability with TrueTime: a transaction picks commit timestamp s no earlier than TT.now().latest and then commit waits until TT.now().earliest > s before releasing locks, so real-time order matches timestamp order. The wait is the clock uncertainty, which the Spanner paper reports as a sawtooth between about 1 and 7 ms, 4 ms most of the time (Spanner paper).

Layer 3The machine

The same ladder exists inside one server. Lamport defined sequential consistency in 1979 for multiprocessors, and real CPUs do not provide it by default. x86 uses total store order: each core has a store buffer, so a later load can complete before an earlier store becomes visible to other cores. ARM is weaker still. Programs that need stronger ordering pay for fences (MFENCE, or the barriers emitted for C++ seq_cst atomics), which drain the store buffer. Cache coherence moves 64-byte lines between cores using protocols in the MESI family, and a cross-core transfer costs tens of nanoseconds.

Across machines the same coordination costs a network round trip plus a durable write: roughly 0.5 ms inside a zone, 1 to 2 ms across zones, and 70 to 150 ms across continents, plus an fsync on each acknowledging replica. A local stale read from memory costs well under a microsecond. That gap of three to six orders of magnitude is why weak models exist.

Why it's built this way

  1. Replicas sit apart, and information cannot cross the gap faster than light, so a replica cannot know about a remote write instantly.
  2. A strong model must therefore wait for coordination on every operation, which costs a round trip and makes the minority side of a partition unavailable.
  3. Most data does not need that, so systems offer a menu and let the caller pay only where an anomaly would matter.
  4. Research has shown that no model much stronger than causal consistency can stay available under partitions, and even causal needs clients to stick to one replica, so causal and session guarantees are the practical ceiling for always-on, geo-replicated reads.

Diagram

flowchart TD
    SS["Strict serializable"] --> L[Linearizable]
    SS --> SER[Serializable]
    L --> SEQ[Sequential]
    SEQ --> C[Causal]
    C --> RYW["Read your writes"]
    C --> MR["Monotonic reads"]
    RYW --> E[Eventual]
    MR --> E

Worked example

A social app stores posts in a primary in us-east with read replicas in eu-west and ap-south. Replication lag is 80 ms p50 and 1.5 s p99. The app serves 40,000 reads per second and 600 writes per second.

A user in Paris posts a comment, gets a 200, and the page refreshes from the eu-west replica 300 ms later. In 5 to 10% of refreshes the comment is missing, because lag exceeded 300 ms. Users report "my comment disappeared". That is a read-your-writes violation.

Fix one: route the author's reads to the primary for 5 s after any write. That adds about 90 ms per read from Paris, but only for recent writers, a small fraction of traffic. Fix two: return the commit log position with the write, store it in the session, and have the replica wait until it has applied that position, up to a 500 ms limit, before answering. Fix two keeps reads local and costs only occasional waiting.

Neither fix makes the system linearizable. A different user in Tokyo may still not see the comment for 1.5 s, which the product accepts.

In the wild

Trade-offs

ModelStrengthsWeaknessesChoose when
LinearizableSimplest to reason aboutQuorum latency; minority unavailableLocks, unique constraints, balances
SequentialGlobal order, cheaper readsReads can be staleConfig and coordination metadata
CausalAvailable under partition, intuitiveDependency tracking overheadGeo-replicated collaboration, feeds
Session guaranteesCheap, fixes most user-visible bugsOnly per clientUser profiles, comments, settings
EventualLowest latency, always writableConflicts, anomaliesCounters, caches, analytics

Pitfalls in production

Leadership lens

Check yourself

  1. How does sequential consistency differ from linearizability?

    Show answer

    Both require a single total order respecting each client's program order, but only linearizability requires that order to match real time. A sequentially consistent read can be stale.

  2. Why is causal consistency attractive for multi-region systems?

    Show answer

    It is among the strongest models that can remain available during a partition (as long as each client sticks to one replica), yet it prevents the anomalies users notice most, like seeing a reply before its parent.

  3. A user updates their avatar and sees the old one on refresh. Which guarantee is broken, and what is a cheap fix?

    Show answer

    Read-your-writes. Pass the write's log position in the session and make replicas wait for it, or route that user's reads to the primary briefly.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →