Spokes.wiki Search About
Tech Article source ↗ source url updated Tue Aug 11 2026 00:00:00 GMT+0000 (Coordinated Universal Time)

Session Revocations at Scale

Canva Engineering (2026-07-22), by Llew Vallis at canva. How Canva serves hundreds of millions of sessions from stateless cookies while still being able to kill one — by moving the revocation deny-list out of MySQL and into Amazon S3 objects that the gateway fleet reads directly, using conditional GETs and conditional PUTs as the only coordination (object-storage-as-coordination).

The problem

Canva’s sessions are stateless: “Canva uses browser cookies to store everything we need to know about a user’s session, from their user ID to their permissions and roles.” No per-request session lookup, and therefore no server-side session to delete. Revoking one means every gateway must carry a deny-list and check each request against it (session-revocation).

The list is bounded by the token lifetime: all tokens refresh within 12 hours, so a revocation older than that can be dropped. Each gateway keeps that 12-hour window in memory — Vallis argues the alternative is worse, because “frequent token refreshes would increase database load” and a refresh path that depends on the database puts availability at risk.

That left the seeding problem. Revocations lived in MySQL, and on every deploy hundreds of gateway pods each pulled more than a million rows at once. The read load scaled with fleet size, not with how many sessions were actually being revoked — a coordinated spike arriving exactly when the fleet was already churning.

The mechanism

  • A 16-byte record. Each revocation packs the principal (user identifier) and a login-timestamp cutoff into 16 bytes, with reserved flag bits for further revocation types. Records are held sorted by principal, so a lookup is a binary search over a plain sorted array. A chunk holding a million revocations is about 16 MB.
  • The window cut into chunks. The 12-hour sliding window is partitioned into 30-minute S3 objects. A starting pod downloads only the chunks it needs — tens of megabytes — rather than querying a database.
  • Conditional GET for propagation. Gateways re-download a chunk only when its content has changed, so steady-state sync costs almost nothing and a new revocation reaches the fleet within minutes.
  • Conditional PUT for writes. An asynchronous worker scans for new revocations, merges them into the current chunk, and uploads it with a conditional PUT — optimistic concurrency control with no lock manager. ZooKeeper leader election cuts down on losing races, but “the PUT conditions guarantee correctness even when races occur”, so the election is an optimization rather than a dependency.
  • Recovery is a download. A gateway rebuilds its entire local state by fetching the relevant chunks, which is the same code path as startup.

They chose S3 over Redis or more MySQL replicas specifically to avoid operating another distributed system for this.

Results (Canva-reported)

  • −87.5% in-memory revocation footprint per gateway.
  • MySQL read replicas cut to two, kept for redundancy rather than for read capacity.
  • Worker sustains over 2,000 revocations/second.
  • Deployments measurably faster, since pod startup no longer stampedes the database.
  • Database load now scales with write throughput and site traffic instead of gateway instance count — the property that actually changed.

What it costs

Propagation is minutes, not instant. A revoked session keeps working on gateways that have not yet pulled the changed chunk, which is a deliberate trade of revocation latency for the removal of a synchronous dependency — and the honest reading of InfoQ’s “near-real-time” framing. The design also inherits S3’s own availability and its per-request cost, and the worker is a single logical writer per chunk, so write throughput is bounded by merge-and-upload rather than by anything horizontal.

The post’s closing methodological note is the transferable part: test the design at scale on real infrastructure before rolling it out, rather than reasoning about object-store behaviour from the docs.

T2 — first-party engineering blog, specific mechanisms and figures, all self-reported and unreplicated.

session-revocation · object-storage-as-coordination · canva · llew-vallis · canva-session-revocation-infoq · modal-1m-sandboxes · zalando-cslb-1m-rps · platform-ops · production-readiness