In the previous chapter, we learned CAP Theorem and saw that distributed systems face a fundamental trade-off during network partitions.
But one important question remains:
What exactly does "consistency" mean?
A distributed system can provide many different consistency guarantees.
For example:
Strong consistency
↓
Linearizability
↓
Sequential consistency
↓
Causal consistency
↓
Eventual consistencyThese are not simply "good" and "bad" versions of consistency.
They represent different guarantees with different costs.
This chapter teaches you how to reason about them and, more importantly, how to choose an appropriate consistency model during system design.
Imagine a database with three replicas:
┌───────────┐
│ Application│
└─────┬─────┘
│
┌────────┼────────┐
│ │ │
┌──▼──┐ ┌──▼──┐ ┌──▼──┐
│ R1 │ │ R2 │ │ R3 │
└─────┘ └─────┘ └─────┘Suppose a write occurs:
Write X = 100Does every replica need to immediately return:
X = 100?
Not necessarily.
Different applications have different requirements.
A stale balance can be dangerous.
A temporarily stale count may be acceptable.
A newly created document may take a short time to appear.
A recommendation generated from slightly stale data may be completely acceptable.
Therefore we need different consistency guarantees.
Think of consistency as answering:
If multiple clients interact with a distributed system, what values are they allowed to observe?
The stronger the guarantee:
More predictable behavior
↓
More coordination
↓
Higher latency / lower availability in some failure casesThe weaker the guarantee:
More freedom for replicas
↓
Less coordination
↓
Potentially better latency and availabilityThere is no universally correct consistency model.
A common beginner-friendly definition is:
Once a write succeeds, subsequent reads see the latest value.
Example:
Initial:
X = 10
Client A:
WRITE X = 20
WRITE succeeds
Client B:
READ X
Result:
20The system does not allow Client B to observe the old value if the consistency contract requires a strongly consistent read.
Strong consistency usually requires coordination between replicas.
One of the most important strong consistency models is linearizability.
Linearizability makes operations appear as if they happen atomically at a single point between their invocation and completion.
Consider:
Client A:
WRITE X = 20
completes
Client B:
READ XThe read should return:
20because the write completed before the read began.
Linearizability respects real-time ordering.
Suppose:
t1 t2 t3
WRITE X=20 ──────────────►
READ XBecause the write completed before the read started:
READ X → 20A result of:
READ X → 10would violate linearizability.
Suppose there are replicas:
A
B
CA write arrives at A.
To provide a strong guarantee, the system may need coordination.
Possible architecture:
Client
│
▼
Leader A
│
├────► B
│
└────► CThe system may need acknowledgements before confirming the operation.
This can increase:
During a partition, strong consistency may require refusing operations that cannot be safely coordinated.
Sequential consistency is different from linearizability.
A sequentially consistent system guarantees that:
The result is equivalent to some sequential ordering of operations that preserves the order of operations issued by each individual process.
The key difference is:
Linearizability
→ preserves real-time ordering
Sequential consistency
→ does not necessarily preserve global real-time orderingThis distinction is important in advanced distributed systems.
Suppose:
Client A:
WRITE X = 1
Client B:
WRITE X = 2If these operations overlap, a sequentially consistent system may choose a valid ordering such as:
WRITE X=1
WRITE X=2or:
WRITE X=2
WRITE X=1as long as the ordering is consistent with each client's own operation order.
Linearizability additionally requires the ordering to respect real-time relationships between non-overlapping operations.
Causal consistency focuses on cause-and-effect relationships.
Suppose:
User creates post
↓
User comments on postThe comment causally depends on the post.
A causally consistent system should not expose:
Commentbefore exposing the corresponding:
Postto a client that observes the causal chain.
Consider:
A writes:
"New product launched"
Then B writes:
"Congratulations!"The second operation depends on the first.
Therefore:
Event A
↓
causes
↓
Event BA system preserving causal consistency maintains this ordering.
Causal consistency is useful for systems such as:
For example:
Post
↓
Comment
↓
ReplyUsers should not observe the reply without the causal context that makes it meaningful.
Eventual consistency is weaker.
A simplified definition:
If no new updates occur and communication continues, replicas eventually converge to the same value.
Example:
t1:
A = 10
B = 10
t2:
A = 20
B = 10
t3:
A = 20
B = 20At t2:
A ≠ BAt t3:
A = BThe system eventually converges.
A common misconception is:
Eventual consistency means anything can happen.
No.
An eventually consistent system can still define rules for:
The guarantee is simply weaker than strong consistency.
Imagine a global social-media follower count.
Region US:
followers = 10,000
Region EU:
followers = 10,015A user in Europe might temporarily see:
10,015while a user in the US sees:
10,000After replication catches up:
Both → 10,015If the product can tolerate temporary differences, eventual consistency can be appropriate.
A very useful session guarantee is:
After a user successfully writes a value, that same user should be able to read their own write.
Example:
User updates profile:
name = "Anubhav"
Write succeeds
User refreshes page
Expected:
name = "Anubhav"Without read-your-writes:
Write succeeds
↓
Read hits stale replica
↓
Old name appearsThis creates a confusing user experience.
One solution is routing the user to a replica known to contain their latest write.
Example:
Load Balancer
│
┌───────┴───────┐
│ │
Replica A Replica B
│
user's latest writeOther approaches include:
Monotonic reads guarantee:
Once a client has observed a value, later reads should not return an older value.
Example:
First read:
X = 20
Later read:
X = 10This can be surprising.
A monotonic-read system prevents the client from moving backward:
20
↓
20
↓
25but not:
20
↓
10Imagine checking a message application:
First refresh:
5 messages
Second refresh:
3 messagesEven if technically possible due to replica lag, users perceive this as broken behavior.
Monotonic reads prevent this kind of backward movement.
Monotonic writes guarantee that a client's writes are applied in the order the client issued them.
Suppose:
Write A:
status = "processing"
Write B:
status = "completed"The system should not apply:
B before Aif the consistency contract requires monotonic writes.
This is particularly useful when operations depend on previous operations.
Session consistency combines useful guarantees for a particular client session.
Common session guarantees include:
Read Your Writes
Monotonic Reads
Monotonic Writes
Writes Follow ReadsThese guarantees can provide a much better user experience without requiring every operation in the entire system to be globally linearizable.
Suppose:
Client reads version 10
↓
Client writes version 11The write should be based on a state at least as recent as what the client observed.
This prevents causality from going backward.
A useful conceptual spectrum is:
Stronger
│
│ Linearizability
│
│ Sequential consistency
│
│ Causal consistency
│
│ Session guarantees
│
│ Eventual consistency
│
WeakerThis is a conceptual model, not a universal strict ordering of every possible consistency protocol.
Different models guarantee different properties.
| Property | Strong / Linearizable | Eventual |
|---|---|---|
| Latest value immediately visible | Yes, under contract | Not necessarily |
| Replica divergence | Minimized/controlled | Expected temporarily |
| Coordination | Usually higher | Often lower |
| Availability during partition | May decrease | Can be prioritized |
| Latency | Can be higher | Often lower |
| Complexity | Coordination complexity | Conflict/reconciliation complexity |
| Typical use | Critical state | Feeds, counters, indexes |
Do not ask:
"Which consistency model is best?"
Ask:
"What consistency guarantee does this operation actually require?"
For example:
Strong consistencyStrong coordinationModerate / eventual consistency may be sufficientEventual consistency is often acceptableEventual consistency is usually acceptableEventual consistency can often workA major system-design insight is:
Consistency can be chosen per workload or operation.
Suppose an e-commerce system has:
E-commerce
│
┌───────────────┼────────────────┐
│ │ │
Payments Inventory Search
│ │ │
Strong Strong EventualThis allows the system to optimize each subsystem independently.
A common architecture is:
Writes
│
▼
Leader
/ \
/ \
Follower FollowerWrites go through the leader.
Followers replicate the state.
This can simplify ordering.
However, reads from followers may be stale.
For example:
Write → Leader
Read → FollowerIf replication has not completed:
Write = 20
Follower = 10The read may return:
10depending on the consistency contract.
One simple approach for stronger reads:
Writes ──► Leader
Reads ──► LeaderAdvantages:
Disadvantages:
Another architecture:
Writes ──► Leader
Reads ──► ReplicasAdvantages:
Disadvantage:
Replication lag
↓
Stale readsThis is where consistency requirements become an architectural decision.
Suppose:
N = 3with:
W = 2
R = 2Then:
W + R > Nbecause:
2 + 2 > 3The read and write sets must overlap.
This can help ensure a read encounters information from a recent write.
But remember:
Quorum overlap alone does not automatically provide every strong consistency guarantee.
The protocol matters.
Distributed systems often attach versions to data.
Example:
value = "A"
version = 10After an update:
value = "B"
version = 11A replica receiving:
version 10after:
version 11can identify that it is stale.
Versioning helps with:
Suppose:
Product:
stock = 10
version = 5Client reads:
stock = 10
version = 5Client attempts:
UPDATE
SET stock = 9,
version = 6
WHERE version = 5If another client already changed version 5 to 6:
WHERE version = 5fails.
The application can retry using the latest state.
This is a common way to prevent lost updates.
A simple conflict-resolution strategy is:
The update with the greatest timestamp/version wins.
Example:
Replica A:
X = A
timestamp = 100
Replica B:
X = B
timestamp = 105Result:
X = BBut last-write-wins has limitations.
Clock differences can make timestamps unreliable if not handled carefully.
It can also discard valid concurrent updates.
Therefore it should not automatically be used for business-critical conflicts.
Vector clocks track causal history.
For replicas:
A
B
Ca vector can represent:
[A: 3, B: 2, C: 5]This helps determine whether one version happened-before another or whether two versions are concurrent.
Conceptually:
Version 1
↓
Version 2means causally related.
But:
Version A
↘
concurrent
↗
Version Bindicates concurrent updates.
Vector clocks are more sophisticated than simply comparing timestamps.
CRDT stands for:
Conflict-Free Replicated Data Type
CRDTs are data structures designed so that replicas can independently update state and still converge under defined conditions.
They are useful for some:
For example, an increment-only counter can often merge independent increments safely.
CRDTs are powerful, but they are not a universal solution for every business transaction.
Imagine two users edit a shared document.
User A → add text
User B → add textThe system should allow concurrent work without requiring every edit to synchronously coordinate globally.
Techniques such as:
can help.
The correct approach depends on the application.
Consider:
Primary Database
│
▼
Search IndexA user creates a product:
Product createdImmediately searching may return:
No resultbecause indexing happens asynchronously.
This is usually acceptable if the system documents or designs around:
Eventual consistencyThe database remains the source of truth.
Caches introduce another consistency problem.
Suppose:
Database:
price = ₹1,000
Cache:
price = ₹900The database is updated:
price = ₹1,100But cache still contains:
₹900The system must define:
Consistency is therefore not only a database problem.
Common pattern:
Application
│
▼
Cache
│
├── hit → return
│
└── miss
│
▼
Database
│
▼
CacheAfter a database update, the cache must eventually be invalidated or updated.
Otherwise users can see stale data.
Asynchronous messaging introduces another consistency challenge.
Suppose:
Order DB
│
└──► Message Queue
│
▼
Notification ServiceThe order may be committed before the notification is processed.
Therefore:
Order exists
Notification not yet sentThis is normal eventual behavior.
Patterns such as Transactional Outbox can make database-to-message publication more reliable.
Consider:
Order Service
│
▼
Order Created Event
│
┌────┼───────────┐
▼ ▼ ▼
Email Search AnalyticsDifferent consumers process the event at different times.
Therefore the system naturally becomes:
Eventually consistentacross many derived views.
The order service remains the source of truth.
In a monolith:
One database
One transactioncan make consistency easier.
In microservices:
Service A → DB A
Service B → DB B
Service C → DB Ca single ACID transaction across everything may be expensive or undesirable.
Therefore architectures often use:
Consider an order workflow:
Order
↓
Payment
↓
Inventory
↓
ShipmentYou should ask:
Can payment be duplicated?
Usually no.Can stock become negative?
Usually no.Can email arrive slightly later?
Usually yes.Can analytics lag by a few seconds?
Usually yes.This leads to different consistency choices.
A useful architectural concept is to define a consistency budget.
Ask:
How stale can this data safely be?
For example:
Payment:
0 seconds
Inventory:
very small / tightly controlled
Product catalog:
seconds may be acceptable
Search:
seconds or minutes may be acceptable
Analytics:
minutes may be acceptableThe exact values depend on the business.
This makes consistency an explicit engineering requirement rather than an accidental property.
Suppose a read needs confirmation from multiple regions:
India
│
▼
Singapore
│
▼
EuropeCross-region coordination can add significant latency.
If the business accepts slightly stale data:
Read local replicacan be much faster.
Therefore:
Strong consistency
↕
Latencyis often a practical architectural trade-off.
This relationship is one of the ideas expanded by PACELC.
During a partition:
Partition
│
├── Strong consistency
│ ↓
│ reject/wait
│
└── Availability
↓
keep servingThe correct decision depends on what the operation means.
For example:
Payment authorization
→ safer to reject than incorrectly authorize
Social feed
→ may be better to serve slightly stale content| Workload | Typical Requirement | Possible Model |
|---|---|---|
| Payment balance | Very strong | Linearizable / transactional |
| Inventory reservation | Strong | Strong coordination |
| User profile | Moderate to strong | Session/strong depending on needs |
| Product catalog | Moderate | Eventual can be acceptable |
| Search index | Eventual | Eventual |
| Analytics | Eventual | Eventual |
| Social feed | Eventual / causal | Causal + eventual |
| Like counter | Eventual | Eventual |
| Collaborative editing | Causal/convergent | CRDT/OT-based |
| Distributed lock | Strong | Linearizable coordination |
These are typical patterns, not universal rules.
Eventual consistency means the user always sees stale data.
Stale data is possible temporarily.
If updates stop and replication succeeds, replicas should converge under the eventual-consistency contract.
Strong consistency means every replica is physically updated before every read.
Not necessarily.
The implementation can use:
What matters is the externally observable guarantee.
Eventual consistency is always faster.
Not necessarily.
Conflict resolution, retries, reconciliation, and application complexity can introduce significant costs.
The trade-off is not simply:
strong = slow
eventual = fastIt depends on the architecture.
Every microservice should use eventual consistency.
No.
Some operations require strong consistency.
A microservice architecture should choose consistency based on business requirements.
A read from a replica is always eventually consistent.
Not necessarily.
A system can provide stronger read guarantees through:
The exact contract matters.
What is linearizability?
Linearizability makes each operation appear to occur atomically at a single point between invocation and completion while respecting real-time ordering between non-overlapping operations.
What is eventual consistency?
If updates stop and communication continues successfully, replicas eventually converge to the same state.
It allows temporary divergence.
What is causal consistency?
Operations that have a causal relationship must be observed in an order consistent with that relationship.
For example:
Post
↓
CommentA system should not expose the comment without respecting the causal dependency.
What problem does read-your-writes solve?
It prevents a user from seeing an older state after successfully writing a newer state.
Example:
Update profile
↓
Read profile
↓
Must see own updateWhat are monotonic reads?
Once a client observes a particular version of data, later reads should not return an older version.
Example:
20 → 20 → 25is valid.
20 → 10violates monotonic reads.
When would you use eventual consistency?
When temporary stale data is acceptable and the system benefits from reduced coordination, lower latency, higher availability, or easier geographic distribution.
Examples include:
Should every operation in a distributed system use the same consistency level?
No.
Different operations can have different requirements.
For example:
Payment → strong
Search → eventual
Analytics → eventual
User session → session guaranteesThis is often a more scalable architectural approach.
Requirements:
Design:
Global Users
│
┌──────────────┼──────────────┐
│ │ │
Region A Region B Region C
│ │ │
Replica A Replica B Replica CDefine:
The important part is to explicitly document the consistency contract.
A possible design:
Client
│
▼
API Layer
│
┌────┴────┐
│ │
Cache Profile DB
│
┌────────┼────────┐
▼ ▼ ▼
R1 R2 R3Write:
Client
↓
API
↓
Primary
↓
Version = 42
↓
ReplicateRead-your-writes can be supported by:
Client receives version 42
↓
Later read requires version >= 42A replica that has only:
version = 40cannot satisfy the read.
The system can:
Before selecting a consistency model, answer:
When approaching consistency in system design, use this sequence:
Business requirement
↓
What can go wrong?
↓
How stale can data be?
↓
Do writes require coordination?
↓
Do reads require latest state?
↓
What happens during partition?
↓
Choose consistency model
↓
Choose replication strategy
↓
Choose read/write routing
↓
Design recoveryDo not start with:
"Should I use CP or AP?"Start with:
"What guarantee does the business operation require?"You now understand the major consistency models used in distributed systems.
The most important concepts are:
Readers observe the latest committed state according to the system's guarantee.
Operations appear atomic and respect real-time ordering.
Operations appear in a valid sequential order while preserving each client's program order.
Causally related operations are observed in causal order.
Replicas may temporarily diverge but eventually converge when updates stop and communication succeeds.
Useful client-oriented guarantees include:
Distributed systems may use:
The biggest system-design lesson is:
Consistency is a business requirement translated into a distributed-systems guarantee.
Explain linearizability using a bank account example.
Explain the difference between:
Linearizability
vs
Sequential consistencyExplain causal consistency using:
Post → Comment → ReplyDesign read-your-writes for a multi-region profile service.
Explain monotonic reads with a social-media example.
A search index can be 10 seconds behind the database.
Which consistency model could be appropriate and why?
A payment service must never expose an incorrect successful balance.
What consistency requirements would you consider?
Design a globally distributed social-media feed.
Requirements:
Identify the consistency model for:
Posts
Feed
Comments
Likes
User's own writesThen explain:
Replication
Caching
Partition behavior
Conflict resolutionThis exercise combines CAP and consistency models.
In the next chapter, we will learn how systems perform operations that span multiple machines or services.
We will cover:
Mark it as complete to track your mastery.