Consistency Models in Distributed
Systems
A practical tour of the guarantees your database might give you
Almost every distributed database, message queue, and storage system describes its behavior in terms of one or
more consistency models. These models are precise statements about what an application can observe when
multiple clients read and write concurrently across multiple replicas. They are also one of the most frequently
misunderstood corners of computer systems. Vendors blur definitions, terms overlap inconsistently across the
literature, and the gap between 'strong consistency' as a phrase and 'strong consistency' as a formal guarantee is
enormous.
This guide is written for engineers who design or operate systems built on distributed storage and need to know
what their tools actually promise. It walks through the major consistency models from strictest to most relaxed,
explains the tradeoffs each implies, surveys how real systems classify, and closes with practical guidance on
choosing and reasoning about consistency in application design.
No prior background in distributed systems theory is assumed beyond familiarity with concurrency and basic
database operations. The treatment is informal but precise; where a formal definition matters, it is stated.
1. Why Consistency Is Hard
In a single-node system, the question of consistency barely arises. There is one copy of the data, one stream of
operations, and a natural total order in which everything happens. Reads see the most recent write because there
is no other write to consider.
As soon as data lives on more than one machine, this comfortable picture falls apart. Replicas may receive writes
in different orders. Network partitions may temporarily disconnect them. A read from one replica may return a
value that has been overwritten on another. Even on a single machine, sufficiently advanced storage engines and
CPU caches introduce reorderings invisible from outside.
1.1 Three sources of trouble
Three forces conspire to make distributed consistency hard. Latency is one: synchronizing every operation across
replicas separated by thousands of miles imposes round-trip costs that no amount of engineering can eliminate.
Partial failure is another: nodes crash, links drop packets, and the system must keep functioning despite a partial
view of its own state. Concurrency is the third: many clients act simultaneously, and the system must impose
some order on their operations or accept that different observers see different orders.
1.2 The CAP theorem and its descendants
Eric Brewer's CAP theorem, formalized by Gilbert and Lynch in 2002, states that a distributed system cannot
simultaneously provide consistency (in the linearizable sense), availability, and partition tolerance. Since
networks always partition eventually, real systems choose between C and A under partition. CAP is often
misused as a design philosophy; it is more precisely a theorem about a specific narrow scenario. The more useful
generalization, PACELC, observes that even without partitions, systems must trade latency against consistency:
stricter guarantees require more coordination, which takes time.
1.3 The point of consistency models
A consistency model is a contract between a storage system and its clients. The system promises that observable
behavior will conform to the model; the application can then reason about correctness under that assumption.
Without a precise model, you cannot tell whether your application is correct, only that you have not yet
encountered a bug. The rest of this guide is a tour through the models you are most likely to encounter in practice.
2. Linearizability
Linearizability is the strongest single-object consistency model in common use. It is also the most intuitive: a
linearizable system behaves as if every operation took effect atomically at some single instant between its
invocation and its response, in an order consistent with real time.
2.1 The definition
Formally, a history of operations is linearizable if there exists a total order on those operations such that the order
respects the real-time ordering of non-overlapping operations and the result of each operation matches what a
sequential execution in that order would produce. The 'respects real time' clause is the key constraint: if operation
A completes before operation B begins (in wall-clock time), then A must appear before B in the linearization.
2.2 What it gives you
Linearizability provides what most engineers intuitively expect from a database: once a write succeeds, every
subsequent read (anywhere, from anyone) sees that write or a later one. There are no stale reads, no out-of-order
observations, no scenario where client A tells client B about a write and client B then fails to see it.
Critically, linearizability is composable: if every individual object in a system is linearizable, the system as a
whole still provides linearizable access to each object. This is unusual; most other consistency models lack this
property and require system-wide reasoning.
2.3 What it costs
Linearizability requires coordination on every operation. In practice this means a consensus round (Paxos, Raft)
or equivalent, involving network round-trips to a majority of replicas. The minimum latency for a linearizable
operation is roughly the round-trip time to the slowest member of that majority. Across regions, this can easily be
100 ms or more.
Linearizability also limits availability: if a majority of replicas cannot be reached, no operation can complete.
This is the C side of the CAP trade.
2.4 Systems that provide it
Etcd, ZooKeeper, and Consul provide linearizable reads and writes and are widely used for configuration and
coordination. Google's Spanner provides linearizability for both reads and writes across global regions, using
carefully synchronized clocks (TrueTime) to make the necessary ordering possible. CockroachDB,
FoundationDB, and YugabyteDB offer linearizable operations as well, with various implementation strategies.
Many systems advertise 'strong consistency' without specifying whether they mean linearizability, sequential
consistency, or serializability. Read the docs carefully; the distinctions matter.
3. Sequential Consistency
Sequential consistency, introduced by Lamport in 1979, is linearizability's slightly weaker sibling. The difference
is small but significant.
3.1 The definition
A history is sequentially consistent if there exists a total order on operations such that each client's operations
appear in the order that client issued them, and the result of each operation matches what that total order would
produce sequentially. Unlike linearizability, sequential consistency does not require the total order to respect real
time across different clients.
In practical terms, if client A completes a write and then tells client B about it over a side channel (phone call,
message bus), client B may still not see that write under sequential consistency. Each client sees a self-consistent
history, and all clients agree on the order, but that order need not match wall-clock reality.
3.2 When it suffices
Sequential consistency is often enough when clients do not communicate out-of-band. If the only signal that
operation A has occurred is reading its effect from the system, sequential consistency and linearizability are
operationally indistinguishable. It is when external communication channels exist that the gap becomes visible.
3.3 Where you find it
Pure sequential consistency without the real-time guarantee is rare as a published model in modern systems; most
that aim for single-object strong consistency reach for linearizability outright. Sequential consistency does show
up in memory models (the original context of Lamport's definition) and in some replicated state machine
implementations.
4. Serializability and Strict Serializability
Linearizability is about single objects. Serializability is about transactions, which span multiple objects. The two
are independent and sometimes confused.
4.1 Serializability
A schedule of transactions is serializable if its outcome is equivalent to some serial execution of those
transactions, one at a time. The classical isolation level SERIALIZABLE in SQL databases means this: the
database may execute transactions concurrently for performance, but the visible result must match some serial
order. The order can be any valid one; it need not match the wall-clock order of transaction submission.
Serializability is the gold standard for transactional correctness. Once you have it, application reasoning collapses
to single-threaded reasoning per transaction. The whole field of database concurrency control exists to provide
this property cheaply.
4.2 Strict serializability
Strict serializability adds the real-time constraint that linearizability has, applied to transactions instead of single
operations. If transaction T1 commits before T2 begins (in wall-clock time), then T1 must appear before T2 in the
serial order. This is the model Spanner and CockroachDB target.
4.3 Common weaker isolation levels
SQL standards define a hierarchy of isolation levels weaker than serializable, each permitting specific anomalies
in exchange for performance. Understanding what each permits is more important than memorizing the names.
Read uncommitted
Permits dirty reads: a transaction may read data written by another transaction that has not committed and may
still abort. Almost no modern database actually offers this; it is a vestigial level.
Read committed
The default in PostgreSQL, Oracle, and SQL Server. A transaction only reads data committed by other
transactions, but successive reads within one transaction may see different values as other transactions commit.
Permits non-repeatable reads and phantom reads.
Repeatable read
Successive reads of the same row within a transaction return the same value. In MySQL InnoDB this is the
default. Permits phantom reads in the SQL standard, though InnoDB's implementation happens to prevent them.
Snapshot isolation
Each transaction sees a consistent snapshot of the database as of some moment, typically its start. Reads never
block writes and vice versa. Snapshot isolation is widely used (PostgreSQL's repeatable read implementation,
Oracle's serializable, many others) but permits the write-skew anomaly: two transactions can each read
overlapping data, write disjoint data, and both commit, producing a result that no serial execution could.
Serializable
Eliminates all the anomalies above. PostgreSQL offers true serializable isolation via Serializable Snapshot
Isolation, which detects dangerous patterns at commit time and aborts one of the conflicting transactions.
Always know your default isolation level. The default in your database may be weaker than you think, and
code reviewed under one assumption may misbehave under another.
5. Causal Consistency
Causal consistency sits below linearizability and sequential consistency but is significantly stronger than the
eventual consistency we will reach later. It captures an intuitive notion: if event A causes event B, every observer
who sees B must also see A.
5.1 The happens-before relation
Causal consistency is built on Lamport's happens-before relation. Operation A happens-before operation B if A is
in the same process as B and precedes it, if A writes a value B reads, or transitively. Operations not related by
happens-before are concurrent.
A system is causally consistent if every client observes causally-related operations in causal order. Concurrent
operations may be observed in any order, and different clients may disagree on the order of concurrent operations.
5.2 What it protects you from
Causal consistency rules out the most disturbing class of anomalies in eventually consistent systems: seeing an
effect without its cause. The canonical example: user A posts a photo, then comments on it. With causal
consistency, no observer can see the comment without also seeing the photo. Without it, that bizarre scenario is
possible.
It is also sufficient for many session-level guarantees that applications expect: read-your-writes (a client sees its
own writes), monotonic reads (a client never observes time going backward), and monotonic writes (a client's
writes are applied in the order issued).
5.3 What it costs
Causal consistency is dramatically cheaper than linearizability because it requires no global agreement. Each
operation carries metadata (vector clocks, dotted version vectors, or similar) tracking its causal dependencies, and
replicas delay applying an operation only until its causal predecessors arrive. There is no blocking on global
consensus; partitioned replicas can keep accepting writes.
5.4 Where you find it
Pure causal consistency systems include COPS, Bolt-on Causal Consistency, and academic prototypes like Eiger.
In commercial systems, causal consistency is more often an option than a default. MongoDB offers causally
consistent sessions. Cosmos DB offers a causal consistency level. Many systems offer something causal-ish with
caveats; read the fine print.
6. Session Guarantees
Between strict global models and unstructured eventual consistency lies a useful set of per-client guarantees
called session consistency. These were named by Terry et al. in the Bayou project and remain a useful
vocabulary.
6.1 Read-your-writes
After a client writes a value, all subsequent reads from that client return that value or a later one.
Trivial-sounding, but easily violated when reads go to replicas that have not yet received the write. Many sharded
or read-replica architectures violate this by default and must be specifically configured to preserve it for sessions
that need it.
6.2 Monotonic reads
Successive reads by the same client return values that respect the real order of writes: a client never sees time go
backwards. If a client reads version 5 and then later reads version 3, monotonic reads has been violated. Replicas
serving the second read are behind the ones that served the first.
6.3 Monotonic writes
Writes from the same client are applied to the system in the order they were issued. Without this guarantee, a
client that issues write A then write B may find them applied in the order B, then A, with B overwritten.
6.4 Writes-follow-reads
If a client reads a value and then issues a write, every observer that sees the write also sees the value the client
read or a later one. This is a per-client form of causal consistency tied to the read-then-write pattern.
6.5 Why session guarantees matter
Most application bugs blamed on 'eventual consistency' are really violations of one of these four guarantees. A
user updates a setting, reloads the page, and sees the old value: read-your-writes violated. A pagination cursor
jumps backwards: monotonic reads violated. The session guarantees often fix the real problems without paying
for full linearizability.
Many systems that nominally provide only eventual consistency can be configured to provide some or all session
guarantees by pinning a session to a single replica or to a quorum sufficient for the specific guarantee. This is one
of the most effective levers in practical distributed systems work.
7. Eventual Consistency
Eventual consistency is the weakest model in widespread use: if no new updates are made, all replicas eventually
converge to the same value. That is the entire promise. No bound on how long convergence takes, no guarantee
about the order in which intermediate values appear, no protection against any of the anomalies covered above.
7.1 What it really means
In practice, 'eventually' is usually milliseconds to seconds for healthy systems and can stretch to minutes or longer
during partitions or load spikes. Eventual consistency is not the absence of consistency; it is a specific, weak
guarantee. Knowing that the system will eventually agree is genuinely useful for some applications and
dangerous for others.
7.2 Convergence and conflict resolution
If two replicas concurrently accept conflicting writes, eventual consistency requires that they eventually resolve
to the same value. Several strategies exist. Last-writer-wins picks one value based on timestamps; it is simple but
loses data and depends on accurate clocks. Application-level resolution returns conflicting values to the
application and lets it merge (the classic Dynamo approach). CRDTs (Conflict-free Replicated Data Types)
define data structures whose merge function is mathematically guaranteed to converge regardless of operation
order.
7.3 Suitable workloads
Eventual consistency suits workloads where stale reads are tolerable and conflicts are either rare or have
well-understood resolution. Caching, analytics, content distribution, presence tracking, and counters are common
examples. It is poorly suited to any workload where 'last value wins' would silently corrupt user data (financial
transactions, inventory) or where read-your-writes is operationally important without explicit handling.
7.4 Systems that default to it
Cassandra, DynamoDB, Riak, S3 (until 2020), Couchbase, and many others default to eventual consistency. All
offer stronger options on a per-request or per-table basis: DynamoDB has strongly consistent reads, Cassandra
has tunable consistency via the QUORUM and ALL levels, and so on. The flexibility is valuable, but it puts the
burden on application authors to pick the right level for each operation.
8. Quorums and Tunable Consistency
Dynamo-style systems achieve a spectrum of consistency levels by varying how many replicas participate in each
read and write. Understanding the underlying math clarifies what these knobs actually do.
8.1 The R + W > N rule
If a system has N replicas, requires W of them to acknowledge each write, and reads from R of them, then setting
R + W > N guarantees that every read sees at least one replica that participated in the most recent write. This is
the classical quorum property and the foundation of tunable-consistency systems.
Typical configurations: N=3, W=2, R=2 (R+W=4, the most common production setting). N=3, W=1, R=1 gives
faster operations but no consistency guarantee. N=3, W=3, R=1 makes writes slow but reads fast and consistent.
N=3, W=1, R=3 is the inverse.
8.2 Why R + W > N is not enough
The quorum rule guarantees overlap on a single value, but real-world Dynamo-style systems still permit
anomalies that linearizable systems do not. Without proper coordination, two concurrent writes can be
acknowledged by overlapping quorums and produce conflicting versions that must be resolved later. Read repair,
sloppy quorums (accepting writes on temporary substitute nodes during partitions), and hinted handoff all
introduce additional ways for the simple R+W>N intuition to fail.
Kleppmann's 'Designing Data-Intensive Applications' has a particularly good treatment of why dynamic quorums
alone do not produce linearizability; the precise gap between quorum systems and true linearizability is subtle and
worth understanding before relying on quorum reads for correctness-critical paths.
8.3 Tunable consistency in Cassandra
Cassandra exposes consistency levels per query. The most relevant:
Level Meaning
ONE Acknowledged by a single replica
QUORUM Acknowledged by a majority
LOCAL_QUORUM Majority within the local datacenter
EACH_QUORUM Majority in every datacenter (writes only)
ALL All replicas; loses availability if any are down
LOCAL_SERIAL Lightweight transactions, local DC
A common production pattern is LOCAL_QUORUM for both reads and writes, which provides quorum semantics
within a region while tolerating cross-region partitions. Cross-region consistency is achieved through replication
and tolerated to be eventually consistent.
8.4 The cost of stronger levels
Higher consistency levels mean more replicas involved, which means higher latency, lower throughput, and
reduced availability under failure. The right level for an operation is the weakest one that satisfies the correctness
requirements of that operation, not the default chosen at the database level.
9. Conflict-Free Replicated Data Types
CRDTs are a class of data structures that allow concurrent updates across replicas without coordination, while
guaranteeing that replicas converge to the same state once they have exchanged updates. They have become a
foundational tool for collaborative applications and a serious alternative to traditional consensus for specific
workloads.
9.1 The basic idea
A CRDT defines operations whose effects, when applied in any order across replicas, produce the same final
state. The mathematical property required is that the merge operation be commutative, associative, and
idempotent. Once a data type satisfies these, replicas can apply updates independently and reconcile lazily.
9.2 Examples
G-Counter (grow-only counter)
Each replica maintains its own count and adds only to its own value. The merged value is the sum across replicas
(with each replica taking the max of its known versions). Increment is trivially commutative.
PN-Counter
Two G-Counters, one for increments and one for decrements. The value is the difference. Permits both addition
and subtraction.
OR-Set (observed-remove set)
Each addition is tagged with a unique identifier. Removal removes all currently observed tags. Concurrent add
and remove of the same element resolve in favor of add, which is usually what users expect.
LWW-Element-Set
Each element carries an add-timestamp and a remove-timestamp. The element is in the set if the most recent
timestamp is an add. Simple but depends on clock accuracy.
RGA (replicated growable array)
A list CRDT where each insertion is anchored to an existing element or to the list start, and order is determined
by the tree of insertions. Underlies many real-time collaborative editors.
9.3 Where CRDTs shine and where they don't
CRDTs shine for high-write, low-coordination workloads: collaborative editing (Figma, Linear, many real-time
apps), presence and cursor tracking, distributed counters, offline-first mobile applications. They scale linearly
with replicas and remain available during partitions.
CRDTs are not suited for workloads requiring invariants the operation set does not preserve. You cannot use a
basic counter CRDT to enforce 'inventory never goes below zero'; concurrent decrements can violate that
invariant after merge. For such constraints, either coordinate or accept compensating transactions.
9.4 Implementations
Major implementations include Automerge and Yjs for collaborative applications, Akka Distributed Data for the
JVM, AntidoteDB as a research database, and CRDT support in Redis Enterprise. Each offers a library of
pre-built CRDTs and tools for building custom ones.
10. Replication Strategies
Consistency models describe what clients observe; replication strategies describe how the system gets there. The
relationship is not one-to-one. The same consistency model can be implemented by different replication schemes,
and the same scheme can support different models depending on configuration.
10.1 Single-leader replication
One replica accepts all writes and replicates them to followers. Reads can come from the leader (linearizable,
with caveats) or from followers (eventually consistent). This is the model behind PostgreSQL streaming
replication, MySQL replication, and many others. It is simple and well-understood; the main weakness is leader
failover, which requires careful orchestration to avoid data loss or split-brain.
10.2 Multi-leader replication
Multiple replicas accept writes and propagate changes to each other. Useful for multi-region deployments where
writes should be fast in each region. The unavoidable cost is conflict handling: concurrent writes to the same data
must be resolved somehow. Common in eventually consistent systems; rare in strongly consistent ones because of
the resolution complexity.
10.3 Leaderless replication
No designated leader. Clients send writes to multiple replicas in parallel and use quorum logic to decide success.
Dynamo, Cassandra, and Riak follow this model. It tolerates node failures gracefully (any replica can serve any
operation) but requires explicit conflict resolution and is more complex to reason about.
10.4 Consensus-based replication
All replicas agree on a totally ordered sequence of operations via a consensus protocol (Paxos, Raft, Zab). Once
an operation enters the log, it is applied identically to every replica. This is the foundation for linearizable
systems like etcd, ZooKeeper, and CockroachDB. Throughput is bounded by the speed of consensus; latency is
bounded by network round-trips to a majority.
10.5 Synchronous vs asynchronous
Orthogonal to the topology is whether replication is synchronous (client waits for replicas to acknowledge) or
asynchronous (client is acknowledged after the primary writes, and replication happens later). Synchronous gives
durability and consistency guarantees at the cost of latency; asynchronous offers low latency at the cost of
possible data loss on failover. Many production systems use a hybrid: synchronous to a few nearby replicas for
durability, asynchronous to distant ones for read locality.
11. Choosing a Consistency Model
In application design, the question is not 'what is the strongest model available' but 'what model does each
operation actually require'. Different operations within the same application often have different requirements,
and good designs match each to its needs.
11.1 Start with the invariants
Identify the invariants the application must preserve. Account balance cannot go negative. A username must be
globally unique. A scheduled meeting cannot overlap with another for the same room. Each invariant has a
coordination requirement: either coordinate (strong consistency) or accept that the invariant may be temporarily
violated and design a compensating mechanism.
11.2 Categorize operations
Most operations fall into a few buckets. Single-key reads and writes are usually safe under eventual consistency
with session guarantees. Conditional operations ('insert if not exists', 'increment if balance ≥ amount') require
strong consistency on the key in question. Multi-key transactions require serializability or an explicit
compensating-transaction pattern. Read-only analytical queries can usually accept stale data.
11.3 Match the storage system
Once you know what each operation needs, match the database. Strong consistency throughout: a system in the
CockroachDB / Spanner family. Mixed needs with strong defaults: PostgreSQL with careful read-replica use.
High-write eventually consistent with occasional strong operations: Cassandra with selective use of lightweight
transactions, or DynamoDB with strongly consistent reads on hot paths.
11.4 Layer carefully
Many systems combine multiple stores: a strongly consistent transactional database for the source of truth, an
eventually consistent search index, a cache, a message queue. Each piece has its own consistency model, and the
system as a whole inherits the weakest part for any operation that depends on multiple stores. Update flows
between stores almost always introduce eventual consistency at the boundaries.
11.5 Beware of accidental coupling
A common mistake is sending a confirmation email or webhook immediately after a write commits, before
downstream consumers (like a search index or cache) have caught up. The user clicks the link, the request goes to
a system that has not yet seen the write, and the user sees an error. The fix is either to delay external notifications
until downstream propagation completes, or to ensure the user's request path goes through the same store that
holds authoritative state.
12. Testing and Verifying Consistency
Consistency bugs are notoriously hard to find because they often manifest only under specific concurrent
schedules or failure patterns. Several tools and techniques exist to surface them.
12.1 Jepsen
Kyle Kingsbury's Jepsen has become the de facto standard for testing distributed systems' consistency claims. A
Jepsen test subjects a database to controlled fault injection (network partitions, clock skew, process kills) while a
workload runs, records all operations, and then checks the recorded history against a specified consistency model.
Many production systems have learned hard lessons from Jepsen reports: Mongo, etcd, Cassandra, CockroachDB,
FaunaDB, and many others have publicly addressed flaws Jepsen surfaced. The reports are also excellent reading
for understanding what consistency means in practice; they go from claim to test to result with unusual precision.
12.2 Linearizability checking with Knossos / Elle
Underlying Jepsen are checkers like Knossos (for linearizability) and Elle (for transactional anomalies). These
can be used independently in unit and integration tests against your own system. Knossos brute-forces possible
linearizations; Elle constructs a dependency graph from observed transactions and detects cycles that indicate
isolation violations.
12.3 Property-based testing
Frameworks like Hypothesis (Python), QuickCheck (Haskell), and their many imitators are well-suited to
consistency testing. Generate random sequences of operations, run them concurrently against your system, and
assert that the observed history satisfies the consistency property you claim.
12.4 Production observability
In production, key signals include replication lag (how far behind are replicas?), conflict rates (how often does the
system detect concurrent updates?), and consistency-level distribution (what fraction of operations request which
level?). Anomalies in any of these often precede user-visible bugs by hours or days.
13. A Map of Real Systems
To anchor the abstract material, this chapter classifies a set of widely used systems by the consistency they offer.
These are default behaviors; most systems have configuration knobs that shift their position on this map.
13.1 Linearizable / strict serializable
etcd, ZooKeeper, and Consul provide linearizable reads and writes for their key-value APIs, backed by Raft or
Zab. Spanner and CockroachDB provide strict serializable transactions across global geographies. FoundationDB
provides strict serializable transactions in a single deployment. These are the systems to reach for when
correctness depends on global ordering.
13.2 Serializable (without strict real-time)
PostgreSQL in serializable isolation mode provides serializable transactions on a single instance. With streaming
replication to followers, reads from followers fall back to weaker guarantees. VoltDB and similar in-memory
transactional systems also offer serializability.
13.3 Snapshot isolation
PostgreSQL repeatable read, Oracle serializable (which is actually snapshot isolation), MySQL InnoDB
repeatable read, SQL Server snapshot isolation, and many others. Useful and efficient for most workloads but
vulnerable to write skew. Some systems offer serializable snapshot isolation as a paid upgrade.
13.4 Causal / session consistency
MongoDB causally consistent sessions, Azure Cosmos DB at its consistent prefix and session levels, MongoDB's
majority read concern with causal sessions. Many systems can be configured for session guarantees by
sticky-routing clients to specific replicas.
13.5 Eventually consistent
Cassandra, DynamoDB (by default), Riak, Couchbase, S3 (older), Elasticsearch, most CDNs, most caches. All
these can be configured for stronger operations on specific paths, but their defaults and scale-out characteristics
assume eventual consistency.
13.6 CRDT-based
Redis Enterprise's active-active databases, Riak's data type support, AntidoteDB, and application-layer libraries
like Automerge and Yjs. Best for collaborative and offline-first applications where availability matters more than
strict ordering.
14. A Note on Distributed Transactions
Most of this guide concerns single-object or single-database consistency. Distributed transactions, which span
multiple databases or services, are a related problem with its own classical and modern solutions. They deserve
their own treatment, but a brief tour is worthwhile here because the consistency vocabulary applies.
14.1 Two-phase commit
Two-phase commit (2PC) is the classical protocol for coordinating a transaction across multiple participants. A
coordinator asks each participant to prepare, gathers acknowledgments, then asks each to commit. If any
participant refuses, all are told to abort.
2PC provides atomicity across participants but has well-known weaknesses: it blocks on coordinator failure
(participants in the prepared state cannot independently decide), it does not survive network partitions well, and
its synchronous nature makes it slow. Modern systems generally avoid 2PC for these reasons, reaching for
consensus-based replication or saga patterns instead.
14.2 Sagas
A saga is a sequence of local transactions, each with a defined compensating action. If any step fails, previously
completed steps are undone by running their compensations. Sagas are not atomic in the ACID sense: other
observers can see intermediate states. They trade atomicity for availability and are appropriate for long-running
workflows where blocking the entire system on a distributed commit would be unacceptable.
Implementing sagas correctly is harder than it looks. Compensating actions must be idempotent, must handle the
case where the original action partially executed, and must account for any external side effects that cannot be
undone (an email sent, a payment captured). The pattern is powerful but unforgiving.
14.3 Outbox pattern
A common practical pattern for keeping multiple stores in sync is the transactional outbox: a service that needs to
update its database and publish an event does both in a single local transaction, writing the event to an 'outbox'
table in the same database. A separate process polls the outbox and publishes events to downstream consumers,
with at-least-once delivery semantics.
The outbox pattern avoids distributed transactions entirely while preserving the key invariant that an event is
published if and only if its causing state change committed. Consumers must be idempotent to handle redelivery,
but that is generally a much easier requirement to meet than full distributed consensus.
14.4 When to reach for which
If your operations naturally fit within a single database that supports serializable transactions, use that. If they
span services but you control all of them, an outbox pattern plus idempotent consumers handles most cases. If
they span services or external systems with significant duration or human approval steps, use sagas with explicit
compensation. Reserve true 2PC for cases where nothing else fits and the systems involved support it well, which
is rarer than it sounds.
15. Quick Reference
15.1 Model strength hierarchy
Model Real-time? Across objects? Typical cost
Strict serializable Yes Yes (txns) Consensus per txn
Linearizable Yes No (single obj) Consensus per op
Serializable No Yes (txns) 2PL / SSI / OCC
Sequential No No Total order, no real-time
Snapshot isolation No Yes (txns) MVCC; permits write skew
Causal Per causal chain No Vector clocks; no consensus
Read-your-writes Per client No Sticky routing / quorum
Eventual No No Asynchronous propagation
15.2 Anomalies permitted by each level
Anomaly Permitted under
Dirty read Read uncommitted
Non-repeatable read Read committed
Phantom read Repeatable read (SQL standard)
Write skew Snapshot isolation
Lost update Most non-serializable levels
Stale read Anything below linearizable
Out-of-order observation Anything below causal
15.3 Vocabulary pitfalls
Some terms are routinely misused in ways that cause real confusion:
Strong consistency has no formal definition. In marketing it usually means linearizable; in academic writing it
sometimes means sequential consistency or even just serializability. Always ask which specific model is meant.
ACID bundles four properties (atomicity, consistency, isolation, durability), but the C in ACID is a
per-application invariant property, distinct from the consistency models discussed here. A database can be ACID
and offer only read-committed isolation; the two C's mean different things.
Read-after-write consistency is sometimes used interchangeably with read-your-writes, but in some vendor docs
it specifically refers to a global property (any read after any write sees that write) closer to linearizability. Check
what is meant in each context.
Eventually consistent tells you nothing about the time bound for convergence, what intermediate states are
observable, or what session-level guarantees apply. It is a floor, not a description.
15.4 Closing thoughts
Consistency models are best understood not as a hierarchy of better and worse but as a set of contracts each suited
to a different mix of correctness, availability, and performance requirements. The engineer's job is to know what
each contract actually says, choose the weakest one that satisfies the application's invariants, and verify that the
chosen system actually delivers what its marketing claims.
Three pieces of advice will serve most engineers well. First, always read the database's documentation on
isolation and consistency carefully; the defaults are rarely what intuition suggests. Second, treat operations
differently: a single application may need linearizable operations for some paths and eventual consistency for
others, and forcing one model on everything wastes either correctness or performance. Third, when claims and
behavior diverge, trust the behavior: Jepsen reports exist because vendors are sometimes wrong about what their
own systems guarantee. The tools to verify are now accessible; use them when the stakes justify it.
The field continues to evolve. Modern systems are increasingly able to offer strong consistency at acceptable cost
where it was once considered prohibitive (Spanner being the canonical example), and the line between
transactional and analytical, between batch and streaming, between strongly and eventually consistent, is
becoming more configurable rather than more fixed. The conceptual vocabulary in this guide should remain
stable even as the specific systems that occupy each region of the map shift.
15.5 Further reading
Several resources go deeper into the territory this guide surveys. Martin Kleppmann's Designing Data-Intensive
Applications remains the best single-book introduction to the practical side of distributed data; chapters 5, 7, and
9 cover replication, transactions, and consistency respectively, with the care this material requires.
The Jepsen analyses at [Link] are essential primary sources for anyone evaluating a specific system; the
writeups combine clear explanations of the consistency claims with empirical evidence of whether the system
actually delivers them. The Hermitage project catalogs how different databases handle the classical isolation
anomalies, with reproducible test cases.
For theoretical foundations, Leslie Lamport's papers on time, clocks, and the ordering of events (1978) and on
sequential consistency (1979) remain readable and foundational. The CRDT literature is well-summarized in
Shapiro et al.'s 2011 paper 'Conflict-free Replicated Data Types.' Vogels' 'Eventually Consistent' (2008) gives a
concise survey of the session guarantees and tunable-consistency landscape from the Dynamo perspective.
Finally, the documentation of the system you actually use deserves careful reading. The most consequential
consistency surprises in practice tend to come not from misunderstanding the theory but from missing a
configuration default or a documented edge case in the specific database in front of you. Reread your storage
system's consistency documentation periodically; both your understanding and the documentation itself tend to
improve.