CONCEPT Cited by 1 source
Stale-while-revalidate cache¶
Definition¶
Stale-while-revalidate (SWR) is a caching semantic where a cache is permitted to serve a stale entry immediately while it revalidates (or re-fetches) in the background. The client gets a bounded-stale response with zero extra latency; the origin sees one refresh request per window rather than a burst of synchronous misses.
Originally codified for HTTP caches as Cache-Control:
stale-while-revalidate=<seconds> in
RFC 5861, it
has become a generic cache-design primitive that shows up
everywhere a cache has (a) a freshness lifetime and (b) a
background-refresh capability:
- HTTP intermediary caches — CDNs, reverse proxies.
- Client-side frameworks —
swr/ React Query on the browser. - Application-tier in-process caches — Caffeine's
refreshAfterWrite, Guava'sLoadingCachewith refresh, Go'sgolang/groupcache. See concepts/async-loading-cache-stale-window for the Caffeine-specific form. - Service-mesh / gateway caches — some proxies emit SWR semantics on upstream cache-control directives.
Why it's useful¶
A plain TTL cache has exactly two states per key: fresh (hit) or expired (miss). Every boundary crossing from fresh to expired is a latency spike + stampede risk. SWR adds a third state — "stale but servable" — between expired and fresh, during which reads are fast and the cache takes responsibility for bringing the value back up to date.
This decouples user-perceived latency from freshness-refresh latency. The origin round-trip happens on a background thread; the serving path never waits on it.
Three common knobs¶
- TTL (fresh lifetime) — up to this point the value is just fresh; no refresh is triggered on read.
- Stale window (or SWR duration) — between TTL and
TTL + stale_window, reads serve the value and also trigger refresh. Some systems (Caffeine) combine TTL and stale window by configuring a shorterrefreshAfterWritealongsideexpireAfterWrite. - Maximum staleness — at some point the stale value is
no longer acceptable; reads block on a fresh fetch.
Implementation-specific; Caffeine uses
expireAfterWriteas the hard ceiling.
Trade-offs vs. alternatives¶
| Alternative | What SWR does better | What SWR does worse |
|---|---|---|
| Plain TTL | No tail-latency spike on expiry; no stampede | Bounded-stale reads instead of strict-fresh-or-miss |
| Write-through | Keeps read latency low without write coupling | Eventually-consistent instead of synchronously updated |
| Manual refresh-ahead scheduler | Doesn't require a schedule; refresh is traffic-driven | Low-traffic keys still take synchronous misses when TTL elapses |
| Event-driven invalidation | No invalidation plumbing; origin-read is self-healing | Can't reflect deletes synchronously; requires TTL as backstop |
Seen in¶
- sources/2025-03-06-zalando-from-event-driven-chaos-to-a-blazingly-fast-serving-api — Zalando's PRAPI uses Caffeine's async-loading cache with a 60-second TTL and a trailing 15-second stale window for this exact reason: in the last 15 s of an entry's life, a read triggers a background DynamoDB refresh while the cached value is returned synchronously. The pattern is load-bearing for PRAPI's single-digit-ms P99 because it absorbs the would-be tail spike of TTL expiry.
- sources/2026-04-21-vercel-preventing-the-stampede-request-collapsing-in-the-vercel-cdn
— Vercel describes the HTTP-level realisation via
Cache-Controldirectives and request-collapsing in the CDN edge (referenced here as a sibling HTTP-altitude instance).
Related¶
- concepts/async-loading-cache-stale-window — the application-cache realisation at the Caffeine altitude
- concepts/cache-ttl-staleness-dilemma — the tension SWR resolves
- concepts/cache-stampede — what SWR also suppresses at near-expiry
- concepts/cache-control-aware-grace-period — a related HTTP-cache primitive for cross-service freshness budgets
- systems/caffeine · patterns/async-refresh-cache-loader