Skip to content
RungsySign in

Cache Invalidation

TTLs, tags, stale-while-revalidate — the genuinely hard half of caching.

40 minDifficulty 4/5cache · architectureAI-writtenWritten by a model on 16 August 2026 and not yet read by a person. Checked automatically: schema, the pedagogical rules the hand-written material is held to, and every diagram parsed for real.

Before this

Why this exists

Writing data into a cache is trivial; deciding exactly when to evict or refresh it without corrupting state or overwhelming downstream databases is notoriously difficult. Relying solely on arbitrary expirations leads to either stale reads immediately after mutations or devastating cache stampedes under heavy traffic.

The mental model

A restaurant specials chalkboard. If the kitchen runs out of halibut, the staff can either wait until closing time for the board to get wiped clean (**TTL**), smudge off the halibut line the second it sells out (**explicit eviction**), or wipe away the entire "Seafood" column using a single shared category header (**tag-based invalidation**).

How it works

TTL Expirations vs. Event-Driven Purging

Time-To-Live (`TTL`) provides passive eventual consistency with zero cross-system coordination, making it resilient to missed messages but tolerant of staleness for the duration of the timer. Event-driven invalidation actively purges or updates entries immediately upon mutation (e.g., via database hooks, CDC streams, or application events), eliminating staleness windows at the expense of requiring high-reliability message delivery.

Cache Stampedes and Probabilistic Early Expiration

When a hot key expires or gets purged, hundreds of concurrent requests experience a cache miss simultaneously and hammer the primary store—a failure mode called the **thundering herd** or **cache stampede**. Mitigation strategies include mutex locking (e.g., using `singleflight` in Go or Redis distributed locks) to allow only one thread to regenerate the value, or algorithms like **XFetch** that trigger asynchronous background refreshes probabilistically before the TTL expires.

Surrogate Keys and Hierarchical Tagging

Direct key invalidation fails when a single entity mutation affects multiple aggregated representations (e.g., an author changing their name impacts their profile, book listings, and review summaries). Modern caches and CDNs (Fastly, Cloudflare) solve this with **Surrogate-Keys** (Cache-Tags). Responses are stamped with multiple tags like `author:42` and `book:108`; when author 42 updates, a single purge call targeting tag `author:42` invalidates every dependent cached response globally.

Stale-While-Revalidate Asynchrony

The `stale-while-revalidate` (SWR) cache-control directive decouples client latency from cache regeneration. When an asset reaches the end of its fresh lifetime, the cache continues serving the stale entry to end users with zero latency while asynchronously kicking off a non-blocking fetch to the origin to update the cache for future requests.

The mechanism

1. **Read Request with SWR**: A client requests `/articles/42`. The edge finds a cached response with `Cache-Control: max-age=60, stale-while-revalidate=300` and `Surrogate-Key: article-42 author-9`. If 70 seconds have elapsed, the edge returns the cached payload instantly and queues a background origin fetch. 2. **Origin Mutation & Invalidation**: A CMS user updates Article 42. The application database transaction commits first. Next, an invalidation event is emitted to the cache layer targeting tag `article-42`. 3. **Tag Soft-Purge**: The cache marks all entries tagged `article-42` as expired rather than deleting them immediately, allowing incoming reads to serve the last-known-good version while a single background worker rebuilds the cache.

sequenceDiagram
  autonumber
  actor Client
  participant Cache as Edge Cache
  participant Origin as App Server / DB

  Client->>Cache: GET /articles/42
  Note over Cache: TTL expired, inside SWR window
  Cache-->>Client: 200 OK (Stale Response)
  Cache-)Origin: Background Async Fetch
  Origin-->>Cache: 200 OK (New Payload + Tags)
  Note over Cache: Updates internal cache entry

  Note over Origin: Mutation: User edits article 42
  Origin->>Cache: PURGE Surrogate-Key: article-42
  Note over Cache: Invalidate all tagged entries
Diagram source for Cache Invalidation.

What people get wrong

Setting a short TTL (e.g., 5 seconds) eliminates the need for explicit cache invalidation logic.
Short TTLs reduce the staleness window but do not eliminate read-after-write inconsistency and dramatically increase database baseline load under high traffic. A user who saves changes and immediately reloads will still see their own old data if the request falls within those 5 seconds, and volatile keys will constantly hammer the database on interval boundaries.
Writing to the database and then deleting the cache key is completely safe from race conditions.
A concurrent read between the database write and the cache delete can read old data from a replica or slow transaction and repopulate the cache with stale data after your deletion. If Read Request A queries the DB right before Write Request B commits, Read Request A might write its stale result to the cache *after* Write Request B executes its cache eviction, leaving stale data in the cache indefinitely until a TTL expires.

When not to use it

Financial transactions or inventory balance checks requiring absolute linearizability and strict ACID guarantees.
Query the primary database directly inside a transaction or use strong distributed consensus engines without intermediate cache layers.
High-cardinality data with low read-to-write ratios (e.g., real-time streaming telemetry where each key is written once and rarely read).
Direct append-only time-series databases or distributed message queues rather than caching layers.

Terms

Thundering Herd (Cache Stampede)
A failure mode where multiple concurrent requests simultaneously miss the cache for an expired key and overwhelm the downstream database with identical expensive queries.
Surrogate Keys (Cache Tags)
Metadata labels assigned to cached HTTP responses allowing multiple distinct URLs to be purged simultaneously with a single identifier.
Probabilistic Early Expiration (XFetch)
An algorithm that causes worker threads to recompute and refresh a cached item before it expires, with the refresh probability rising as the expiration time approaches.

Can you recall it?

Why does the 'stale-while-revalidate' caching pattern prevent thundering herd problems during cache invalidations?

Keep track of this

Add Caching & Alternative Stores to your map and Rungsy will schedule reviews so you actually remember it.