Consistency Models and User Guarantees

LESSON

Distributed Systems Foundations

009 20 min beginner

Consistency Models and User Guarantees

Core Insight

Mina edits the bio on her profile from “Researcher” to “Independent researcher.” The app shows Saved, then immediately reloads the profile. The new wording matters less than the expectation it creates: after the app says the change succeeded, Mina should not be shown the older bio as if the save never happened.

The service stores profiles in several regions. The write goes to the profile owner in Madrid, while the reload may be routed to a nearby follower in Frankfurt. At that moment, the important question is not how many replicas exist. It is what that second read is allowed to observe. If the follower has only applied an older version, a fast response can still violate the meaning of “Saved.”

A consistency model is a promise about observations: which results a read may return after one or more writes, and for whom that promise holds. The promise can be narrow, such as “the person who made the edit sees it on their next read.” Or it can be broad, such as “once a credential change succeeds, every later authentication check sees the new rule.” Those are different guarantees with different costs.

The useful design move is to start with the surprise that must not happen. Then choose the least expensive mechanism that prevents it. “Use a consistent database” is not a usable requirement. “After this user saves their bio, their session must see version 52 or newer” is one. It names the actor, the state, and the forbidden observation.

Name The Observation Before The Guarantee

For Mina's profile, the relevant state is a small record with a version:

profile/mina
  version 51: bio = "Researcher"
  version 52: bio = "Independent researcher"

The system has several visible pieces:

Mina's app       sends a write and later reads
profile owner    accepts the official next version
followers        apply copies of that version, possibly later
router           chooses where each read goes
session token    carries evidence of what Mina has already seen

Before choosing a formal term, write the product sentence:

After Mina is told that profile version 52 was saved,
her later profile reads in this session must return version 52 or newer.

This sentence does not require every person on the internet to see version 52 immediately. It protects Mina's own experience. A visitor in another region might still see version 51 briefly if the product can tolerate that. The scope is part of the guarantee, not a detail to add later.

Compare it with a password reset. If the system says an old password or API key has been revoked, allowing that old credential at a different authentication server is usually a security failure, not a mildly stale page. The product sentence is stronger:

After password version 12 is confirmed,
every later authentication decision for this account must reject version 11.

Both workflows use replication. They should not automatically use the same observation promise.

Worked Trace: Carry A Minimum Version Through A Session

The profile service uses a leader or write owner plus followers. Its current replicated position for Mina's record is version 51.

Madrid owner:       profile/mina at version 51
Frankfurt follower: profile/mina at version 51
Mina's app:         no minimum version yet

1. Accept The Write And Return Evidence

Mina submits the new bio. The Madrid owner records version 52 and returns a response only after the service's chosen write-durability rule has completed. The response includes the version Mina's app is now entitled to see.

Mina -> Madrid owner: PUT profile/mina, bio = "Independent researcher"

Madrid owner:
  version 51 -> version 52
  replicate version 52 to followers

response to Mina:
  saved_version = 52

The app stores saved_version = 52 in the session or attaches it to future reads. It is not a clock timestamp and does not claim that every follower has caught up. It is evidence of a requirement: this session must not be served a version older than 52 for this profile.

2. A Follower Receives The Later Read

Mina reloads from a network location that the router sends to Frankfurt. The follower is still at version 51.

Mina -> Frankfurt follower: GET profile/mina, minimum_version = 52

Frankfurt follower:
  applied version = 51
  required version = 52
  result: version 51 is not an allowed response

The follower now has a few correct choices. It can wait briefly for replication to apply version 52. It can route the read to the owner. Or it can return a retryable response that preserves the session guarantee. It must not quietly return version 51 just because it is locally available.

Frankfurt applies version 52
Frankfurt -> Mina: version 52, "Independent researcher"

This is read-your-writes: after a user's accepted write, that user's later reads observe that write or something newer. The mechanism can be session affinity, a minimum-version token, a leader read, or a quorum read. The formal name describes the observation; the implementation is a separate design choice.

3. Prevent The Session From Moving Backward

Later, Mina's app reads a newer profile version, 54, after changing another field. A load balancer then tries to use a different follower that has only applied version 52.

Mina has already observed: version 54
new follower can serve:   version 52
allowed?                  no

Returning version 52 would make the profile appear to revert. Monotonic reads prevent that kind of time travel within an observation path: once a session has seen a newer version, later reads must not return an older one. A session can update its required minimum from 52 to 54 and use the same wait, route, or retry choices.

This is not a promise that every user sees the newest value. It is a promise that one observer's view does not move backward. That narrower guarantee can often cost much less than making all reads globally current.

Guarantees Protect Different Kinds Of Surprise

Consistency terms are easy to blur because they all restrict stale or reordered observations. Their protected experiences differ.

Read-your-writes protects the writer: “I saved it, so I should see it.” It is often scoped to a user session, device, or token. A profile editor is a common fit.

Monotonic reads protect an observer from regression: “Do not show me version 54, then version 52.” It matters for dashboards, document views, delivery tracking, and any interface where backward movement is confusing.

Causal consistency protects dependencies: “Do not show an effect without the cause it depends on.” If Mina's colleague reads her updated bio and then writes a comment saying “the new title looks good,” other observers should not see the comment before the profile update that motivated it. The system carries or reconstructs causal evidence so dependent events remain in a sensible order. It does not require unrelated edits to have one global order.

Linearizability protects a stronger claim: operations act as if they occur in one order consistent with real time. Once a successful write completes, every later read of that object sees the write or a newer one. It is useful when one official answer matters, such as a lock, an inventory decrement, an account balance decision, or a credential revocation.

read-your-writes: "my later read includes my accepted write"
monotonic reads:  "my view does not go backward"
causal:           "effects do not appear before their causes"
linearizable:     "later operations see one real-time order"

These are not interchangeable levels on a simple slider. A system can provide read-your-writes for profile editing, causal order for conversations, and a linearizable rule for password revocation. The scope may be a key, a session, a workflow, or a whole operation. State the scope before choosing a mechanism.

How The Promise Changes The Data Path

The placement design from the previous lesson tells us where data lives and which replica may answer. Consistency requirements tell the router and replicas when a particular answer is good enough.

For Mina's profile session, a token or sticky route can make a follower wait until it reaches the needed version. This preserves a session-level promise while still allowing other ordinary reads to use nearby replicas.

For causal order in a messaging product, a reply can carry metadata that identifies the message version it depends on. A replica that has the reply but not the earlier message waits, fetches the missing dependency, or withholds the reply. The cost is extra metadata and the possibility of waiting for an earlier event.

For a credential reset, the service may route all decisions through an authoritative security store, require a coordinated commit before returning “reset complete,” or maintain a revocation check that every authentication path must consult. The cost is more coordination, higher tail latency, and reduced availability when the authority cannot be reached. The stronger response text must be earned by a stronger path.

Eventual convergence is still useful. It means replicas that stop receiving new conflicting writes will eventually become the same. It does not by itself tell Mina what her next read may show, how long a follower can lag, or whether an authentication server can accept the old password. Convergence is a background property; user guarantees need an explicit observation rule.

Failure Modes And Trade-offs

The common failure is saying “Saved” after a write reaches one place, then routing the user's next read to an older copy. The data may converge later, but the interface has already broken its implied contract. Caches can create the same failure if a cache key has no version, no invalidation policy, and no way to respect the session's minimum version.

Another failure is protecting only the original device. If Mina saves on a laptop, a session token on that device can enforce read-your-writes there. A security-sensitive change used by another device or service needs a broader guarantee. Do not claim that a local session mechanism solves a global revocation requirement.

A third failure is paying for global order when the product does not need it. Requiring every profile view to coordinate with the owner may increase latency and reduce availability for a benefit that a session guarantee would provide more cheaply. Conversely, treating an exclusive purchase as an eventually convergent counter can create promises that repair cannot honor.

Useful operational signals expose whether the chosen guarantee is practical:

The trade-off is specific rather than moral. Stronger guarantees reduce particular surprises, but they spend routing constraints, metadata, coordination, waiting, or availability. Weaker guarantees can make ordinary reads fast and resilient, but only when the product names the stale behavior honestly and has a safe repair story.

Design Check

Pick one workflow: profile editing, a message reply, a shopping-cart update, a bank transfer, a password reset, a dashboard metric, or notification read state. Without looking back, write:

state and key:
writer and later reader:
forbidden observation:
scope of the promise (session, users, services, or everyone):
smallest suitable guarantee:
evidence carried by the read or write:
what a stale replica must do instead of returning bad data:
latency or availability cost:
metric that would reveal a violation:

Then change the scope. If the writer's own session is protected, ask whether a different device must also be protected. If the answer is yes, a session token alone is not enough. That distinction turns a consistency label into a defensible system contract.

Resources

Key Takeaways

PREVIOUS Replication, Partitioning, and State Placement NEXT Gossip, Membership, and Dissemination