Consistency Models in Distributed Systems: Strong, Eventual, and Read-Your-Writes
Learn how strong consistency, eventual consistency, read-your-writes, monotonic reads, and causal consistency affect real APIs, replicas, caches, and user experience.
27 min read
The Update Succeeded — But the User Still Sees the Old Value
Imagine a user changes their profile name.
The request succeeds:
PATCH /profile
→ 200 OKThe database primary now contains:
displayName = "Faisal"Then the frontend immediately loads the profile again.
But the API reads from a replica that has not received the latest update yet.
The user sees:
displayName = "Faisal"The flow looks like:
Client
↓
UPDATE primary database
↓
success
Client
↓
GET profile
↓
read replica
↓
old valueNothing necessarily failed.
The write succeeded.
The replica is healthy.
The system simply gave the user a weaker consistency guarantee than they expected.
This is what consistency models are about.
What Does Consistency Mean?
In distributed systems, the same logical data may exist in several places.
For example:
Primary database
Read replica
Redis
Search index
Event projection
CDN
Browser cache
Client-side stateThese copies may not all update at exactly the same time.
So the real question becomes:
After a write succeeds, which later reads are guaranteed to see that write?
That is a consistency guarantee.
Consistency Is Not One Setting
Developers sometimes think:
strong consistency
or
eventual consistencyas if the entire application must choose one.
Real systems often use different guarantees for different operations.
For example:
User profile after edit
→ read-your-writes
Product search
→ eventual consistency
Permission check
→ authoritative current state
Analytics dashboard
→ bounded stale data
Payment balance
→ strong transactional stateThe right guarantee depends on what the data is used for.
Start With the User Experience
Before choosing database settings, ask:
What is the user allowed to observe?Useful questions include:
Must everyone immediately see
the latest committed value?Must a user at least see
their own latest update?Can another user briefly
see an older version?Can reads move backward
from a newer value to an older one?How stale can the data be
before it becomes incorrect?These are much more useful questions than simply asking:
"Should we use eventual consistency?""Eventually Consistent" Is Not Enough as a Requirement
Suppose someone says:
"The search index is eventually consistent."That still leaves important questions unanswered.
Does eventually mean:
100 milliseconds?
5 seconds?
5 minutes?
1 hour?What happens if the update never appears?
What does the UI show while waiting?
How do we detect that the projection is stuck?
A useful product requirement should say something closer to:
New products should normally appear
in search within 5 seconds.or:
The user must always see
their own profile update immediately.Now the consistency guarantee can actually be designed and monitored.
Strong Consistency
Let's start with the easiest model to reason about.
Under strong consistency, once a write has been acknowledged, later reads behave as though there is one current value.
Example:
Write:
price = 120
↓ acknowledged
Later read:
price = 120The client should not see:
price = 100after the successful write.
Strong Consistency Mental Model
Think:
There is one latest committed value.Once the system confirms the write:
everyone reading under
that guarantee sees
the latest committed stateConceptually:
Write v10
↓
commit
↓
Read
↓
v10not:
Write v10
↓
commit
↓
Read
↓
v8Where Strong Consistency Is Useful
Strong guarantees are easier to reason about for operations such as:
account balances
permission checks
inventory allocation
state transitions
financial recordsbecause stale information may cause incorrect decisions.
Example: Inventory
Suppose only one item remains.
Current inventory:
stock = 1Customer A buys it.
The system commits:
stock = 0If Customer B immediately reads an old replica showing:
stock = 1and the application trusts that stale value for allocation, we may oversell.
For this kind of decision, stronger coordination may be necessary.
Strong Consistency Has a Cost
Distributed copies must coordinate before answering certain requests.
That can require:
leader coordination
quorum reads/writes
routing to primary
waiting for replicationThis can increase:
latency
cross-region traffic
coordination costIt can also reduce availability during certain network failures.
So stronger consistency is not automatically better.
It is a trade-off.
Strong Consistency Does Not Automatically Make the Application Correct
This is important.
Suppose we have a strongly consistent PostgreSQL database.
Our code does:
Read balance = 100
↓
Check if purchase of 80 is allowed
↓
Another request also reads 100
↓
Both subtract 80We may still create an incorrect result if concurrency is not handled properly.
Strong consistency does not replace:
transactions
constraints
locking
optimistic concurrency
business invariantsConsistency model and transaction correctness are related but different topics.
Eventual Consistency
Now consider a system with several derived copies.
Suppose an order becomes paid.
Primary database:
order = paidBut immediately afterward:
Read replica
→ order = pending
Redis
→ order = pending
Search index
→ order missing
Reporting projection
→ order = pendingLater, replication and event processing catch up.
Eventually:
Primary = paid
Replica = paid
Redis = paid
Search = paid
Projection = paidThat is eventual consistency.
Eventual Consistency Mental Model
Think:
The system may temporarily disagree,
but the copies should converge.Example:
Time T0
Primary: v10
Replica: v9
Cache: v8
↓
Time T1
Primary: v10
Replica: v10
Cache: v10During the gap, different users may observe different states.
Why Use Eventual Consistency?
It allows parts of the system to update asynchronously.
For example:
Order committed
↓
event emitted
↓
email projection
search index
analytics
reportingThe main write does not need to wait for every downstream system.
That can improve:
response latency
decoupling
scalability
availabilityExample: Search Index
Suppose a product is created in PostgreSQL.
The write succeeds immediately.
Then:
ProductCreated event
↓
search index consumer
↓
Elasticsearch/OpenSearchThere may be a delay.
So:
GET /products/123may find it immediately from PostgreSQL.
But:
GET /search?q=...may not show it for a few seconds.
This can be perfectly acceptable if documented and monitored.
Eventual Consistency Needs Honest Intermediate States
Suppose payment processing is asynchronous.
Bad UI:
Payment request accepted
↓
show "Payment confirmed"even though provider confirmation has not arrived yet.
That creates a promise the architecture cannot guarantee.
Better:
awaiting_confirmationThen later:
confirmedor:
failedThe user sees the real state.
Example Order Status
type OrderPaymentStatus = "awaiting_confirmation" | "confirmed" | "failed";Flow:
Order created
↓
awaiting_confirmation
↓
payment webhook
↓
confirmedThis makes eventual consistency visible instead of hiding it.
Eventual Consistency Needs Repair
What happens if a consumer misses an event?
Suppose:
Order database
→ paidbut search consumer crashes.
Search remains:
pendingforever unless there is a recovery mechanism.
Good eventual systems usually need:
replayable events
idempotent consumers
retries
dead-letter handling
reconciliation
lag monitoringEventual consistency should mean:
eventually convergesnot:
hopefully convergesReconciliation
A reconciliation job compares:
source of truthagainst:
derived stateExample:
Orders DB:
order 123 = paid
Projection:
order 123 = pendingThe reconciliation process can detect the mismatch and repair it.
This is especially useful for important projections.
Read-Your-Writes Consistency
Read-your-writes is one of the most useful user-facing guarantees.
It means:
After I successfully change something, my later reads should not show an older version.
Example:
User changes name:
Faisal
→ Faisal
API returns success.
Next read by same user:
FaisalThe user should not see their own change disappear.
Why Users Expect Read-Your-Writes
Users may tolerate another person seeing slightly stale data.
But this feels broken:
User clicks Save
↓
"Saved successfully"
↓
page reload
↓
old value appearsFrom the user's point of view:
Did the save fail?Even though the backend technically succeeded.
Read-your-writes is often a better product requirement than global strong consistency.
Problem With Read Replicas
Suppose architecture is:
Writes
→ primary
Reads
→ replicasFlow:
UPDATE profile
→ primary v10
→ success
immediate GET
→ replica still at v9
→ old profileReplication lag creates the problem.
Solution 1: Return the Updated Representation
Often the easiest solution is:
Do not immediately read again.If the write already returns the updated record:
const updated = await primaryDb.user.update({
where: {
id: userId,
},
data: {
displayName,
},
});
return {
user: updated,
};The frontend can update local state from this response.
Flow:
PATCH profile
↓
primary returns updated object
↓
frontend uses that objectNo immediate replica read is needed.
This Does Not Fully Solve Later Reads
The user might navigate to another page.
That page could still request:
GET /profileand hit a lagging replica.
So returning the representation improves the immediate experience, but a stronger read-your-writes guarantee may require routing logic.
Solution 2: Temporarily Read From Primary
After a write, route that entity's reads to the primary for a short period.
Example:
PATCH profile
→ primary
next GET profile
within 5 sec
→ primary
later reads
→ replicas againThis is simple conceptually but increases load on the primary.
Solution 3: Version Token
Suppose every user record has:
version = 42After the write:
return {
user: updated,
readConsistencyToken: updated.version,
};Client sends:
minimum version = 42on the next read.
Server checks whether the replica has caught up enough.
Conceptually:
Client requires v42
↓
Replica has v44?
↓
yes → read replicaor:
Client requires v42
↓
Replica has v39?
↓
no → read primaryThis gives more precise routing.
The Token Does Not Need to Be Literally an Entity Version
Depending on the database, consistency might be tracked using:
record version
LSN
replication position
commit timestamp
sequence numberThe important idea is:
client tells server:
"Do not give me anything older
than what I already wrote."Solution 4: Session Stickiness
Another approach is to keep the user on the same region or replica.
For example:
User writes in Region A
↓
subsequent reads
stay in Region Aor:
requests remain pinned
to Replica 3This can reduce consistency surprises.
But problems appear during:
failover
replica restart
multi-device usage
region changesSo stickiness is useful but not a complete universal solution.
Multi-Device Complication
Suppose the user changes their profile on:
phoneThen immediately opens:
laptopIf read-your-writes state exists only in the phone's session:
laptop may still read stale replicaIf cross-device consistency matters, the guarantee must be tied to server-side state or data versioning rather than only one client session.
Monotonic Reads
Another useful consistency guarantee is monotonic reads.
It means:
Once a client has seen version 9, later reads should not return version 7.
Bad experience:
Read 1:
order = version 9
Read 2:
order = version 7Time appears to move backward.
How Can Reads Move Backward?
Suppose:
Replica A = version 9
Replica B = version 7First request:
GET
→ Replica A
→ v9Second request:
GET
→ Replica B
→ v7The user now sees older data than before.
Monotonic Read Solutions
Possible strategies:
session affinity
version token
replica progress check
primary fallbackFor example:
Client has seen v9.
Next read requires:
version >= 9.If replica only has v7:
do not use itRead-Your-Writes vs Monotonic Reads
These are related but different.
Read-your-writes:
I wrote v10.
My later read should not return
older than v10.Monotonic reads:
I already saw v10.
My later read should not return
v8.You can need one, both, or neither depending on the product.
Causal Consistency
Causal consistency is about preserving cause-and-effect relationships.
Suppose:
A:
Create discussion thread
B:
Add reply to threadOperation B only makes sense because A happened first.
Seeing:
reply existswhile:
thread does not existwould violate that causal relationship.
Another Causal Example
A:
Create user
B:
Create order for userIf another service sees:
order createdbefore:
user createdit may fail or create confusing state.
The system should preserve the required business ordering.
Global Ordering Is Usually Too Expensive
You could try to create one global event sequence:
event 1
event 2
event 3
event 4
...across the entire company.
Usually unnecessary.
Most business rules only need ordering within a smaller scope.
For example:
Order 123 events
must remain ordered.But events for:
Order 123do not necessarily need ordering relative to:
Order 999This allows much better scalability.
Preserve Ordering by Aggregate Key
For event systems, a common approach is partitioning by entity ID.
Example:
Kafka partition key:
orderIdEvents:
order.created
order.paid
order.shippedfor the same order land in the same ordered partition.
This preserves the order that actually matters.
Causation Metadata
Events can also carry metadata such as:
eventId
causationId
correlationId
entityVersionExample:
PaymentConfirmed
caused by
PaymentRequestedThis helps consumers understand relationships and detect out-of-order or duplicate processing.
Consistency Exists at Many Layers
A common mistake is checking only database replication.
Suppose PostgreSQL primary is perfectly consistent.
The user can still see stale data from:
Redis
search index
CDN
browser cache
frontend query cache
event projectionSo consistency is an end-to-end property.
Example Full Read Path
Client
↓
Browser cache
↓
CDN
↓
API
↓
Redis
↓
read replicaAny of these layers can return stale data.
Changing PostgreSQL read settings alone may not fix the issue.
Typical Staleness Sources
| Layer | Why it becomes stale |
|---|---|
| Read replica | Replication lag |
| Redis | TTL or missed invalidation |
| Search index | Async indexing |
| Projection | Consumer lag/failure |
| CDN | Cache TTL |
| Browser | HTTP cache |
| Client state | Stale local/query cache |
When investigating stale data, trace the entire path.
Example: Database Is Current, Redis Is Not
Suppose:
PostgreSQL:
price = 120
Replica:
price = 120
Redis:
price = 100Reading from the primary will not help if the request hits Redis first.
The stale layer is the cache.
Example: Redis Is Current, Client Is Not
Suppose:
PostgreSQL = v10
Redis = v10
API response = v10but React Query still contains:
v9and the component uses cached client state.
The backend is correct.
The user still sees stale data.
This is why consistency must be understood across the full system.
Consistency for Event-Driven Projections
Suppose:
Primary order DB
↓
OrderPaid event
↓
analytics projectionThe projection may lag behind.
This is normal if the requirement is eventual consistency.
But you need to know:
How far behind?Projection Lag
Useful measurements include:
event created at:
12:00:00
projection updated at:
12:00:04Convergence delay:
4 secondsNow we can define an SLO:
99% of order projections
updated within 10 seconds.That is a useful eventual-consistency contract.
Pending States Make Async Systems Easier to Understand
Suppose an API triggers an asynchronous operation.
Bad:
POST /orders
→ says completedwhile downstream processing is still happening.
Better:
status = processingThen:
processing
→ completedor:
processing
→ failedHonest intermediate states align the API with the real consistency model.
When Is Staleness Fine?
Many reads do not require the latest value.
Examples:
marketing content
product search results
recommendations
analytics dashboards
view countersIf these are:
a few seconds behindthe product may still work perfectly.
Using stronger consistency everywhere may add cost with little user value.
When Is Staleness Dangerous?
Staleness becomes a correctness problem when a decision depends on current state.
Examples:
permissions
account balance
inventory reservation
payment status transition
resource ownership
fraud lockFor these operations, the application may need to read authoritative current state.
Display Read vs Decision Read
This is a useful distinction.
Display read:
Show product availability
on catalog page.Maybe slightly stale is acceptable.
Decision read:
Reserve the last available unit.That should use authoritative transactional state.
Example: Permissions
Suppose:
User is admin.Redis caches permissions for:
30 minutesAdmin privilege is revoked.
Database:
admin = falseRedis:
admin = trueIf a sensitive command trusts stale Redis:
user may still perform
admin actionsThat is a security bug.
Cache Can Be Fine for UI Permissions
The UI may cache:
show/hide admin buttonfor convenience.
But the command itself should still validate against an authoritative source appropriate for the security requirement.
The frontend is not the authorization boundary.
Example: Payment Balance
Suppose UI dashboard shows:
balance ≈ 10,000from a slightly stale projection.
Maybe okay.
But when executing:
withdraw 9,500the transaction must validate against authoritative balance state.
Again:
display consistency
≠
decision consistencyPractical Consistency Classification
| Read | Typical guarantee |
|---|---|
| Marketing content | Bounded staleness |
| Product search | Eventual |
| Profile immediately after edit | Read-your-writes |
| Notification feed | Eventual / monotonic |
| Permission check | Authoritative |
| Account balance decision | Strong transactional state |
| Analytics dashboard | Eventual / bounded staleness |
These are examples, not universal rules.
The business requirement still decides.
A Practical User Profile Design
Suppose users update:
display name
avatar
bioArchitecture:
Writes
→ primary PostgreSQL
Reads
→ read replicasA practical strategy:
PATCH /profile
1. Write to primary.
2. Return updated profile.
3. Return entity version.
4. Client updates local state.
5. Near-term reads require
at least that version.
6. Fall back to primary
if replica is behind.This gives read-your-writes without forcing every user in the world to read from the primary all the time.
Example Versioned Response
{
"user": {
"id": "user-1",
"displayName": "Faisal",
"version": 42
},
"readConsistencyToken": 42
}Next request:
GET /profile
X-Minimum-Version: 42Conceptually, the server can ensure:
returned version >= 42A Practical Order Projection Design
Suppose order processing uses events.
Order service
↓
order.paid event
↓
projection consumer
↓
customer order-history viewThe write model may already say:
paidwhile the customer history projection says:
pendingfor a short time.
Possible design:
After payment:
return authoritative order state
from command endpoint.
Order-history page:
eventual projection.
If projection lag is high:
show processing state
or fetch authoritative detail.Different endpoints can expose different guarantees.
Version Every Entity Change
Entity versions are useful for:
ordering
optimistic concurrency
monotonic reads
projection updates
duplicate detectionExample:
Order v7
→ paid
Order v8
→ shippedA consumer receiving:
v8then later:
v7can detect the regression.
Projection Consumer Example
Suppose current projection:
version = 8and a delayed event arrives:
version = 7The consumer can ignore it.
Pseudo-rule:
if incomingVersion <= storedVersion:
ignoreThis helps protect derived views from out-of-order delivery.
Event Consumers Should Be Idempotent
Event systems may deliver the same message more than once.
Suppose:
OrderPaid v8arrives twice.
The projection should remain:
paid v8not apply some side effect twice.
Idempotency is essential for reliable eventual consistency.
Replayability Matters
Suppose the search projection is corrupted.
If events are replayable:
event history
↓
rebuild projectionThis gives the system a recovery path.
Without replay or reconciliation, a stale projection may remain incorrect indefinitely.
Observe Convergence
If your architecture uses eventual consistency, measure how quickly data converges.
Useful metrics include:
replica lag
consumer lag
projection age
cache age
indexing delay
version mismatchDatabase Replica Lag
For replication, measure:
primary position
vs
replica replay positionor time-based lag.
Example:
Replica A
→ 50 ms behind
Replica B
→ 4 seconds behindNow routing decisions can be smarter.
Event Consumer Lag
Example:
Latest event:
offset 1,000,000
Consumer:
offset 990,000Lag:
10,000 eventsBut message count alone can be misleading.
Also track:
oldest unprocessed event agebecause 10,000 events could represent:
100 msor:
2 hoursdepending on throughput.
Cache Age
A cache hit is not automatically good.
Suppose:
hit rate = 99%but cached data is:
30 minutes oldwhen business requirement is:
maximum 30 secondsThe cache is performing well technically and failing the product requirement.
Track freshness too.
Search Index Lag
For search:
product created at:
12:00:00
searchable at:
12:00:03Index convergence:
3 secondsThis is much more useful than simply saying:
search is eventually consistentAlert on Meaningful Lag
Nonzero lag is often normal.
You do not need an alert because a replica is:
20 milliseconds behindif your product allows:
2 secondsof staleness.
Alert when the user-facing guarantee is threatened.
For example:
search indexing lag > 10 seconds
for 5 minutesnot:
replica lag > 0Failure Handling for Eventual Systems
Suppose the projection consumer fails.
Your system needs to answer:
Do we continue accepting writes?Maybe yes.
Then projection lag grows.
At some threshold you might:
show stale state
disable a feature
route to source of truth
reject new workEventual consistency should have explicit degraded behavior.
Stronger Consistency Can Be Scoped
You do not need to make the entire system strongly consistent.
For example:
Order command
→ primary
Order immediate detail
→ primary
Order-history list
→ replica/projection
Analytics
→ eventualOnly the paths that require stronger guarantees pay the coordination cost.
Consistency Per Endpoint
A useful API design exercise is to document:
POST /profile
→ authoritative write
GET /profile after own write
→ read-your-writes
GET /users/:id by other users
→ eventual within 2 sec
GET /permissions
→ authoritative
GET /search
→ eventual within 5 secNow engineers know what each endpoint promises.
Consistency and API Responses
HTTP status alone does not define consistency.
Suppose:
POST /exports
→ 200 OKbut actual export generation happens asynchronously.
That response may imply stronger completion than reality.
Better:
202 Acceptedwith:
status = pendingThe API should communicate the true state of asynchronous work.
Consistency and Optimistic UI
Frontends sometimes immediately show the new value before server confirmation.
Example:
User clicks Like
↓
UI increments count
↓
server request happens laterThat is optimistic UI.
It improves responsiveness, but client state may temporarily differ from authoritative server state.
If request fails:
UI must rollbackor reconcile later.
Client-side consistency is part of the whole design.
Consistency and Caches
Suppose database provides read-your-writes.
But the endpoint first checks:
Rediswith a stale value.
Then the guarantee is broken.
If you promise:
read-your-writesevery layer involved in the read must honor that promise.
Possible solutions:
invalidate cache on write
version cache values
bypass cache after write
use consistency tokensConsistency and CDNs
Suppose profile data accidentally uses:
Cache-Control: publicat the CDN.
Even if your database and Redis are perfect:
CDN may return stale copyConsistency guarantees must include caching headers and edge infrastructure too.
Strong vs Eventual Is Not the Only Choice
There is a useful spectrum:
Strong consistency
Read-your-writes
Monotonic reads
Causal consistency
Bounded staleness
Eventual consistencyThe names describe different guarantees.
Real systems combine them.
Bounded Staleness
Bounded staleness means:
The data may be stale, but only up to a defined limit.
Example:
analytics dashboard
may be at most
60 seconds behindThis is often more useful than vague eventual consistency.
It creates a measurable product requirement.
Example Bounded Cache
Suppose:
product metadatamay be:
up to 30 seconds staleThen:
TTL = 30 secplus reliable invalidation may be acceptable.
But if Redis serves:
10-minute-old valuethe SLO is violated.
Common Consistency Mistakes
Mistake 1: Saying "Eventually Consistent" Without a Bound
This gives no usable product guarantee.
Define expected convergence.
Mistake 2: Reading From Replicas Immediately After a Write
If the user expects their own update immediately, provide read-your-writes.
Mistake 3: Fixing Database Consistency While Cache Is Stale
Trace the full read path.
Mistake 4: Making Everything Strongly Consistent
This adds coordination where the product may not need it.
Mistake 5: Using Stale Data for Critical Decisions
Display paths and command decisions may need different sources.
Mistake 6: No Version Metadata
Without versions, detecting regressions and out-of-order updates is harder.
Mistake 7: No Repair Mechanism
An eventual system without retries, replay, or reconciliation may never converge.
Mistake 8: Hiding Async Work Behind "Success"
Expose pending/processing states honestly.
A Simple Decision Process
For every important read, ask:
1. What happens if this read
is one version behind?If answer is:
nothing importanteventual may be fine.
If answer is:
user becomes confusedconsider read-your-writes or monotonic reads.
If answer is:
money/security/inventory
can become incorrectuse authoritative state and stronger transactional guarantees.
Then Ask Who Needs Freshness
Maybe:
the writer needs fresh databut other users can temporarily see stale data.
Then:
read-your-writesmay be enough.
You do not necessarily need global strong consistency.
Then Ask How Long Staleness Is Acceptable
For example:
0 sec
1 sec
5 sec
1 minThis lets you design:
replica routing
cache TTL
index SLO
consumer lag alertaround a concrete target.
Then Ask How the System Recovers
If projection becomes stale:
retry?
replay?
reconcile?
rebuild?If there is no recovery mechanism, "eventual" may become "permanently inconsistent."
Practical Production Architecture
A mature system may intentionally use several guarantees:
Commands
→ primary database
Critical decisions
→ authoritative read
User's immediate post-write read
→ primary / version-aware replica
General read traffic
→ replicas
Search
→ async index
Analytics
→ eventual projections
Public content
→ CDN/cacheEach path chooses the weakest consistency model that is still correct for its use case.
Production Checklist
Before shipping a distributed read/write path:
□ Define the source of truth.
□ Document the consistency guarantee
per endpoint.
□ Decide which reads may be stale.
□ Define maximum acceptable staleness.
□ Return updated representations
from writes when possible.
□ Provide read-your-writes
for user-facing updates.
□ Prevent reads from moving backward
when monotonic behavior matters.
□ Use version or replication tokens
where necessary.
□ Route sensitive reads
to authoritative state.
□ Preserve business-required event order.
□ Avoid unnecessary global ordering.
□ Carry entity versions
through events/projections.
□ Make consumers idempotent.
□ Make events replayable
when projections matter.
□ Add reconciliation
for important derived state.
□ Use honest pending/processing states.
□ Trace the entire read path:
DB, replica, cache, CDN, client.
□ Monitor database replica lag.
□ Monitor event consumer lag.
□ Monitor projection age.
□ Monitor search indexing delay.
□ Monitor cache freshness.
□ Measure time from accepted write
to visible/readable state.
□ Alert on product-relevant
convergence thresholds.
□ Do not use stale state
for sensitive decisions.
□ Test slow replication.
□ Test delayed consumers.
□ Test stale cache behavior.
□ Test multi-replica reads.
□ Test failover and recovery.Conclusion
Consistency is not simply a database configuration.
It is a promise about what users and services are allowed to observe after data changes.
Strong consistency says:
Once the write succeeds,
later reads see the latest
committed value.Eventual consistency says:
Different copies may temporarily disagree,
but they should converge.Read-your-writes says:
After I successfully change something,
I should not immediately see
my old value again.Monotonic reads say:
Once I have seen version 10,
do not later show me version 8.Causal consistency says:
If B depends on A,
do not expose B without A.The important lesson is that these guarantees can coexist inside the same system.
For example:
Profile edit
→ read-your-writes
Search
→ eventual
Permissions
→ authoritative
Analytics
→ bounded staleness
Payment state
→ strong transactional correctnessThe right question is not:
"Should our system be strongly consistent or eventually consistent?"
A better question is:
"For this specific operation, what stale state can the user safely observe without breaking correctness or trust?"
Then make sure every layer involved:
primary database
replica
Redis
search index
projection
CDN
browser
client cachecan actually honor that promise.
That is the real meaning of consistency architecture.
For concrete failure modes, see why read replicas can return stale data immediately after a write and how cache invalidation changes the freshness contract.
References
Related
Caching Architecture: Cache-Aside, Write-Through, Write-Behind, and Redis Patterns
System Design34 min read
Your API Works Fine at 100 RPS… What Actually Breaks at 10,000 RPS?
System Design8 min read
You Added Read Replicas to Scale Reads… So Why Are Users Seeing Old Data After an Update?
Database7 min read
Written by
Faisal
Software engineer writing about backend systems, Node.js, system design, scalable applications, and modern web and mobile development.