Skip to content

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 frameworksswr / React Query on the browser.
  • Application-tier in-process caches — Caffeine's refreshAfterWrite, Guava's LoadingCache with refresh, Go's golang/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

  1. TTL (fresh lifetime) — up to this point the value is just fresh; no refresh is triggered on read.
  2. 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 shorter refreshAfterWrite alongside expireAfterWrite.
  3. Maximum staleness — at some point the stale value is no longer acceptable; reads block on a fresh fetch. Implementation-specific; Caffeine uses expireAfterWrite as 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

Last updated · 501 distilled / 1,218 read