Consistency Models

Session Guarantees: The Underrated Middle Ground

Four properties — read your writes, monotonic reads, monotonic writes, writes follow reads — scoped to a single session. They cost a small amount of client-carried state, they require no agreement between nodes, and together they eliminate nearly every consistency complaint a user actually files.

▶ Run the lab

The question this answers

The question

What is the cheapest set of guarantees that makes a replicated system stop feeling broken to its users?

The guarantee — the property claimed, and its scope

Four per-session properties. Read your writes: a read sees this session's prior writes. Monotonic reads: successive reads in a session never go backwards. Monotonic writes: this session's writes are applied in the order issued. Writes follow reads: a write made after reading a value is ordered after the write that produced that value. All are scoped to one session and say nothing about other sessions.

Everything below is bought to hold this sentence. "Strongly consistent" with no scope attached is a slogan, not a guarantee — read what it actually covers, and what it explicitly does not.

What a node knows — observation versus inference

A replica knows its own applied state and can compare it against a requirement the client presents. It has no idea what the client saw previously unless told. Every one of these guarantees therefore works by the *client* carrying a small piece of order information forward — which is why they need no coordination between nodes, and why losing that token silently downgrades the system to eventual consistency.

A node knows its own state and the messages that arrived. Everything else is inference from evidence that was already stale. "B has not replied in five seconds" is knowledge; "B is down" is a decision — and usually the bug.

What guarantee?What does a node know?How does it work?What can fail?How does it fail?Where is coordination?What holds under failure?How does it recover?How would you know?What is the simpler thing?
session guaranteesread-your-writesmonotonicclient-centric

The four, and the complaint each one prevents

These come from the Bayou work of Terry and colleagues, and their framing is what makes them useful: they are client-centric. Rather than describing what the system does globally, each describes what one session may observe. That reframing is why they are cheap — the enforcement point is the session, not the cluster.

Each maps directly onto a bug report you have seen. Notice that the first two are read-side and are by far the most commonly implemented; the second two are write-side and are quietly assumed by application code far more often than they are provided.

GuaranteeThe bug it preventsMechanism
Read your writesprotocol"I saved it and it is not there"Session carries the position of its last write; reads require at least it
Monotonic readsprotocol"I refreshed and it went backwards"Session carries the highest position it has read; reads require at least it
Monotonic writesprotocol"My second edit applied before my first"Writes from a session are ordered, typically by routing them through one node or sequencing them per session
Writes follow readsprotocol"My reply appears before the comment I replied to"A write inherits the dependencies of what the session read before it
The four session guarantees

One token does most of the work

The first two guarantees are the same mechanism with two sources: a session-scoped floor, raised by writes (giving read-your-writes) and by reads (giving monotonic reads). One number, updated in two places, and replicas that decline to serve below it. That is the whole implementation, and it is developed in detail in Read-After-Write: Letting a User See Their Own Change and Monotonic Reads: Never Let Time Run Backwards.

The write-side pair is less commonly implemented and more commonly assumed. Monotonic writes is what makes "set name, then set avatar" apply in that order — without it, two writes from one session can land on different replicas and be reordered, and the user's second edit can lose to their first. Writes follow reads is the one that makes replies coherent: it says a write that was informed by a read must be ordered after that read's source. It is the session-scoped shadow of causal consistency, and it is what prevents your reply propagating to a replica that has not yet seen the comment.

Together the four give a session a *causally coherent view of its own activity*. They do not give causal consistency across sessions — another user's reply can still overtake the comment it replies to from your perspective. That gap is the whole difference between session guarantees and Causal Consistency: Never Show an Effect Before Its Cause, and it is also the reason session guarantees are so much cheaper: the metadata is one session's worth, not the world's.

1interface Session {
2 floor: number // highest position observed: reads + writes
3 lastWrite: number // for monotonic writes ordering
4}
5
6async function sessionRead(s: Session, key: string) {
7 const replica = pickReplicaAtLeast(s.floor) // read-your-writes + monotonic reads
8 const { value, position } = await replica.read(key)
9 s.floor = Math.max(s.floor, position) // monotonic reads for the NEXT read
10 return value
11}
12
13async function sessionWrite(s: Session, key: string, v: unknown) {
14 // writes-follow-reads: the write depends on everything this session has seen
15 const { position } = await leader.write(key, v, {
16 after: s.floor, // writes follow reads
17 sessionSeq: s.lastWrite + 1, // monotonic writes
18 })
19 s.lastWrite += 1
20 s.floor = Math.max(s.floor, position)
21 return position
22}
All four from one session context

Why these are underrated

Teams facing user-visible staleness tend to jump straight to linearizability, because it is the guarantee everyone can name. That is usually paying global coordination to solve a local problem. The overwhelming majority of consistency complaints are about the complainer's own activity — their save, their refresh, their edit, their reply. Session guarantees address exactly that set, cost a token and a comparison, and require no node to talk to another node.

They also degrade gracefully in a way strong models do not. If the session token is unavailable, you fall back to routing that session to the leader: slower, correct, no outage. If a quorum is unavailable in a linearizable system, you return errors. The failure mode of a session guarantee is latency; the failure mode of linearizability is unavailability.

The honest limitation is scope. Session guarantees say nothing about what one user sees of another user's writes, and nothing about any invariant. If your problem is "two users must not claim the same handle", these are irrelevant and you need coordination. Knowing which of the two problems you have is the actual skill. See Choosing a Consistency Model: Start From the Invariant.

  • Enforced at the session, so no inter-node agreement and no availability cost.
  • Fail to latency (route to the leader), not to unavailability.
  • Cover the complaints users actually file, which are overwhelmingly about their own actions.
  • Cover nothing about cross-user visibility or global invariants — a different problem needing different tools.

Key points

  • Four client-centric properties: read your writes, monotonic reads, monotonic writes, writes follow reads.
  • Read-side pair is one session floor raised by both reads and writes; write-side pair orders a session's writes and their dependencies.
  • Together they give a session a coherent view of its own activity — not causal consistency across sessions.
  • They need no agreement between nodes, so they cost latency rather than availability when stressed.
  • They address the complaints users file; they address no invariant at all.

The chain, answered

Every field here is required, which is why no lesson in this domain can recommend a design without naming what an operator sees when it fails, what survives the partition, what repairs it afterwards, and the simpler thing to consider first.

How it works
  • The session carries a floor: the highest log position it has written or read.
  • Reads present the floor; a replica below it defers to a fresher replica or to the leader.
  • Every read and write raises the floor to the position it observed or produced.
  • Writes carry a per-session sequence number so a replica can apply one session's writes in issue order.
  • Writes also carry the session's current floor as a dependency, so a write informed by a read is ordered after that read's source.
What can fail at the boundary
  • The token is lost — new device, cleared storage, session store eviction — and the guarantees silently vanish.
  • The token is not propagated through an internal service hop.
  • Positions become incomparable after a failover, so the floor is meaningless.
  • A session's writes are routed to different replicas and reordered, breaking monotonic writes.
  • The session concept does not match the user: two devices, two sessions, one confused person.
How it fails — what an operator sees
  • Silent downgrade: the session store is unavailable, requests proceed with no floor, and the system reverts to eventual consistency with no signal. Users see the old bugs return during unrelated incidents.
  • Cross-device incoherence: the phone shows the new value and the laptop shows the old, because the session was scoped to a connection rather than to the user identity.
  • Reordered edits: a user changes a field twice quickly, the two writes land on different nodes, and the earlier value wins. Observed as an edit that "reverted itself", with both writes present in the log in the wrong order.
  • Orphaned reply: a user replies to a comment and the reply appears on a replica that has not received the comment, because writes-follow-reads was not implemented. Observed as replies with no parent in some regions.
  • Leader overload: the fallback path routes too many sessions to the leader during a lag event, and a consistency mechanism becomes a capacity incident.
Where coordination is required
  • None between nodes. The comparison is local and the ordering information travels with the client.
  • The session store is a small shared dependency; plan its unavailability as "fall back to the leader", never as "proceed without the floor".
  • Compare with linearizability, which requires coordination on every operation to deliver a guarantee most users would not notice. See Coordination Couples Availability.
What still holds under failure
  • During replica lag, sessions with high floors are routed to the leader — slower, still correct.
  • During a partition, a session whose floor exceeds every reachable replica cannot be served consistently; the honest response is an explicit error, not a stale value.
  • Sessions that have not written or read recently are unaffected, so the blast radius is limited to active users.
How it recovers
  • Detect: measure the rate of reads that waited or redirected due to the floor, and the fraction of requests arriving with no floor at all.
  • Contain: cap the wait and prefer leader fallback, converting replica lag into leader load rather than user-visible latency.
  • Recover: as replicas catch up, redirects fall away automatically.
  • Reconcile: invalidate floors after a failover changes log lineage rather than comparing incomparable positions.
  • Verify: synthetic probes for all four properties — write-then-read, read-then-read, write-then-write, read-then-write-then-read-elsewhere.
How you would know
  • Fraction of requests carrying a session floor — a drop is a silent downgrade.
  • Redirect-to-leader rate and its contribution to leader load.
  • Client-side regression counter for monotonic reads.
  • Out-of-order application count for per-session write sequences.
  • Orphan rate for dependent records (a reply whose parent is absent on the serving replica).
When it helps
  • Any user-facing application on a replicated store — which is most of them.
  • Systems that moved reads to replicas and started receiving unreproducible "it did not save" reports.
  • Multi-region deployments where users move between regions and expect their own history to be coherent.
  • As a first step before considering stronger models, since they are cheap enough to be worth trying first.
When it hurts
  • Leader-only read paths, where all four already hold and the plumbing is pure cost.
  • Batch, analytical or machine-to-machine workloads with no session and no human comparing successive results.
  • Problems that are actually about invariants or cross-user visibility, where these guarantees will not help and will delay the real fix.
Simpler alternatives
  • Route everything to the leader: all four hold trivially, and it is correct until leader capacity matters.
  • Causal consistency, if the requirement extends across sessions. See Causal Consistency: Never Show an Effect Before Its Cause.
  • Linearizability, if the requirement is a genuine invariant rather than a coherence complaint. See Linearizability: An Operation Is an Interval, Not a Point.
  • Optimistic client rendering, which fixes the read-your-writes symptom in the UI without any server change.
  • Bounded staleness, coarser but simpler where per-session tracking is impractical.

The cheapest four promises that stop a system feeling broken

The cheapest four promises that stop a replicated system feeling broken
None of these requires nodes to agree with each other. Each is a small piece of order information the client carries — which is why they cost latency rather than availability.
The complaint this prevents — currently firing
“I saved it, the page reloaded, and my change is gone.”
Without the token there is no mechanism at all: a session failing to see its own prior write. is simply permitted.
twhooperationresult
t0sessionPUT /doc → leader200 OK (position 1)
t3sessionGET /doc → farold (position 0)
The complaint it preventsWhat the client carriesWhat it does not give you
Read your writestypical“I saved it, the page reloaded, and my change is gone.”The highest position it has written or observedAnything at all about other sessions, and any invariant
Monotonic readstypical“I refreshed and the comment disappeared again.”The highest position it has written or observedAnything at all about other sessions, and any invariant
Monotonic writestypical“I renamed it twice and it kept the first name.”A per-session sequence numberAnything at all about other sessions, and any invariant
Writes follow readstypical“My reply appears above the message I replied to.”The positions its reads observedAnything at all about other sessions, and any invariant
One token does most of the work: the read-side pair is a single floor raised by both reads and writes
These are underrated because they address the complaints users actually file, at a price that is close to free: no node has to agree with any other, so nothing here fails on the minority side of a partition. The read-side pair collapses into one session floor; the write-side pair orders a session's own writes and the writes that respond to what it read. Together they give a session a coherent view of its own activity — which is not causal consistency across sessions, and is not any kind of invariant. The real work is defining the session: scope it to the user rather than the connection, or the same person on a second device breaks the guarantee you just paid for.
simplifiedThe two read-side traces are read off a three-replica propagation run; the two write-side traces are authored, because they are statements about ordering rather than about lag. All four are per-session and say nothing whatsoever about other sessions.

What people believe, and what is true

Claim

Session guarantees are a weaker version of causal consistency.

Reality

They are causal coherence restricted to one session's own operations. The restriction is exactly what makes the metadata a single number instead of a vector over all writers.

Claim

Sticky sessions provide them.

Reality

Stickiness pins you to a replica, not to a replica with your data, and the pin breaks at deploys, restarts and reconnects. It is a heuristic; a floor is a guarantee.

Claim

If we have read-your-writes we are fine.

Reality

Read-your-writes alone permits successive reads going backwards, permits your two edits applying out of order, and permits your reply propagating ahead of its parent. Four bugs, four guarantees.

Claim

These require a special database.

Reality

They require a position returned on writes and reads, a place to keep the session's floor, and a read path that respects it. Many teams build all three in an afternoon on an ordinary replicated store.

Go deeper

Only the levels this lesson can honestly fill — a missing level is a claim nobody had.

Overview

Four promises to one session: see your own writes, never go backwards, apply your writes in order, and order your writes after what you read.

Practical

Implement the read-side pair first with a single session floor scoped to the user rather than the connection — that fixes most complaints. Add per-session write sequencing and dependency-carrying writes when reordered edits or orphaned replies show up. Never let a missing floor silently mean "no requirement".

Advanced

The four are projections of the causal order onto a single session, which is why they compose into causal coherence for that session and stop precisely at its boundary. That boundary is also the cost boundary: one session's order is a scalar, the world's order is a vector over writers. Session guarantees are therefore not a compromise between eventual and causal consistency — they are causal consistency with the quantifier narrowed, and the narrowing is what makes them nearly free. See Causal Consistency: Never Show an Effect Before Its Cause and Vector Clocks: Buying Concurrency Detection at O(N).

Apply it

Interview questions
  • 💬 Name the four session guarantees and the specific user complaint each one prevents.
  • 💬 Why do session guarantees remain available during a partition when linearizability does not?
  • 💬 You have implemented read-your-writes and users still report edits reverting. Which guarantee is missing?