CONCEPT Cited by 1 source
Session-cookie read-your-writes window¶
Definition¶
A session-cookie read-your-writes window is the mechanism of
using a short-lived per-user cookie (or equivalent session-state
marker) to pin that user's reads to the primary database for Δ
seconds after a write — ensuring the user sees their own just-
committed data even though the global read pool is eventually
consistent and would otherwise return pre-write state.
It is the concrete mechanism behind read-your-writes consistency when the underlying architecture is read-write split with replication lag between primary and replicas.
Canonical invocation¶
From Rails' ActiveRecord::Middleware::DatabaseSelector:
config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
"This tells Rails to send all reads to our read-only region and writes to our primary. After each write, it will set a cookie that will send all reads to the primary for 2 seconds, allowing users to read their own writes." (Source: sources/2026-04-21-planetscale-introducing-planetscale-portals-read-only-regions.)
Mechanics¶
- On every write for session
S, the middleware sets a cookie (or updates a session attribute) containing a timestampt_written = now(). - On every read for session
S, the middleware checks: ifnow() - t_written < Δ, route the read to primary; else route to the read replica pool. - The cookie is per-user: other users continue to hit the
replica pool normally; only the user who just wrote pays the
in-region (if the replica was near them) or cross-region (if
primary is far) RTT for the next
Δseconds.
Correctness condition¶
For the mechanism to provide RYW, the window Δ must exceed the
longest replication lag the user might encounter. In practice:
- Same-region replicas:
Δ = 1–2 sis usually ample; healthy MySQL / Postgres replication lag is sub-second. - Cross-region replicas (
regional read replicas):
Δshould cover cross-region p99 lag, which can be multiple seconds under load. The Portals post's 2 s default is optimistic for a globally-deployed app; 5–10 s is safer.
If Δ is too small, the user occasionally experiences ghost-
reads: the write commits, the cookie expires before the replica
catches up, the next read hits a still-stale replica, the write
"disappears". If Δ is too large, the user reads from primary
unnecessarily and loses the read-latency benefit of replicas for a
small extra window.
Why a cookie (and not a token / LSN)?¶
A session cookie is the coarsest-possible RYW mechanism, and that's its selling point:
- Zero plumbing cost. No LSN / GTID /
$clusterTimetoken threaded through every ORM call, no per-query opaque handle. The middleware is ~20 lines of code in Rails; the cookie is set automatically; the routing decision is local. - Works with any replica topology. Doesn't care whether the
replica pool is 2 same-region replicas or 5 regional replicas or
a hybrid. The decision is "read from primary for
Δseconds" — independent of which replica would otherwise have served the read. - Correct in steady state. If replication is healthy and
Δis well-chosen, RYW holds.
Tradeoff vs. token-based schemes (MongoDB $clusterTime, anything
using LSNs): the cookie is coarser — it pins all reads, not just
reads that need to see the write. A user who writes then navigates
to a read path that doesn't depend on the write still pays the
primary-read tax for Δ seconds. For most interactive web apps,
this is negligible.
Failure modes¶
Δtoo small for p99 lag: classic "my edit disappeared" bug. Hard to reproduce because it only fires during replication stalls. Diagnostic: graphΔagainst replica-lag p99; if they overlap, users will hit ghost-reads.- Cookie missing on some request paths: e.g. API requests from clients that don't send cookies, server-to-server internal traffic, agents. These paths always read from replica and bypass RYW — usually fine (the agent doesn't care about its own writes), sometimes a silent bug.
- Multiple tabs / devices: the cookie is per-device-session. User writes on laptop, reads on phone — phone's session has no cookie, reads from replica, may see stale data. Usually acceptable: cross-device staleness is a UX tolerance most apps have.
- Write rolled back, cookie still set: the middleware sets the
cookie on write request, not write commit. A rolled-back
write still pins reads to primary for
Δ. Harmless (primary reads are always correct) but wastes replica capacity.
Framework analogs¶
- Rails:
ActiveRecord::Middleware::DatabaseSelector(above). - Django:
DATABASE_ROUTERS— route decision is a method call; session cookie check is in the router. - Laravel:
DB::connection('read')with a sticky-option that reads from write-connection for the rest of the request; Laravel's built-in sticky reads per-request rather than per-session, which is weaker (page reloads after a write go to replica; Rails' session cookie covers subsequent page reloads too). - Roll-your-own (plain mysql2 / pg): set a cookie on write, check the cookie in a query-router function, pick primary vs. replica pool accordingly.
Seen in¶
- sources/2026-04-21-planetscale-introducing-planetscale-portals-read-only-regions — the canonical named source; introduces the 2-second default and the session-cookie mechanism for a cross-region Rails + PlanetScale deployment.
Related¶
- concepts/read-your-writes-consistency — the property this mechanism enforces.
- concepts/replication-lag — the distribution
Δmust cover. - concepts/read-write-splitting — the architecture that creates the problem.
- concepts/regional-read-replica — the shape that amplifies it.
- patterns/session-cookie-for-read-your-writes — the pattern page for this mechanism.
- systems/ruby-on-rails — canonical framework-level
implementation (
DatabaseSelector). - systems/planetscale-portals — productised context for the pattern.