The question this answers
What is the cheapest set of guarantees that makes a replicated system stop feeling broken to its users?
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.
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.
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.
| Guarantee | The bug it prevents | Mechanism |
|---|---|---|
| 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 |
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 + writes3 lastWrite: number // for monotonic writes ordering4}5 6async function sessionRead(s: Session, key: string) {7 const replica = pickReplicaAtLeast(s.floor) // read-your-writes + monotonic reads8 const { value, position } = await replica.read(key)9 s.floor = Math.max(s.floor, position) // monotonic reads for the NEXT read10 return value11}12 13async function sessionWrite(s: Session, key: string, v: unknown) {14 // writes-follow-reads: the write depends on everything this session has seen15 const { position } = await leader.write(key, v, {16 after: s.floor, // writes follow reads17 sessionSeq: s.lastWrite + 1, // monotonic writes18 })19 s.lastWrite += 120 s.floor = Math.max(s.floor, position)21 return position22}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.
- • 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.
- • 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.
- • 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.
- • 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.
- • 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.
- • 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.
- • 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).
- • 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.
- • 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.
- • 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
| t | who | operation | result |
|---|---|---|---|
| t0 | session | PUT /doc → leader | 200 OK (position 1) |
| t3 | session | GET /doc → far | old (position 0) |
| The complaint it prevents | What the client carries | What 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 observed | Anything at all about other sessions, and any invariant |
| Monotonic readstypical | “I refreshed and the comment disappeared again.” | The highest position it has written or observed | Anything at all about other sessions, and any invariant |
| Monotonic writestypical | “I renamed it twice and it kept the first name.” | A per-session sequence number | Anything at all about other sessions, and any invariant |
| Writes follow readstypical | “My reply appears above the message I replied to.” | The positions its reads observed | Anything at all about other sessions, and any invariant |
What people believe, and what is true
Session guarantees are a weaker version of causal consistency.
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.
Sticky sessions provide them.
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.
If we have read-your-writes we are fine.
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.
These require a special database.
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
- 💬 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?