Part 3 · 3 chapters · ~18 min

Caching

Cache topology from client to database, the five patterns, invalidation strategies, stampedes and their cures, negative caching and cache penetration, Redis as the workhorse, consistency between cache and database and the dual-write problem, and materialised views as precomputation.

8

Topology and patterns

Every layer between the user and the database can cache: the client (HTTP caching, TanStack Query), the CDN, the application process, a distributed cache, and the database's own buffer pool. Each layer is a replica allowed to be stale (Distributed Systems part 11 covers the mechanics). This part is about choosing which layers a given piece of data may live in, and for how long.

FIVE CACHING PATTERNS
who reads and writes the cache, and when
cache-asideApp reads cache; on miss reads DBand fills cache. Writes go to DB,then delete the key.read-throughThe cache library loads from theDB on a miss. Same consistency ascache-aside, less app code.write-throughWrites go to cache and DBsynchronously. Reads are fresh;writes pay twice.write-behindWrites go to cache; flushed to DBlater in batches. Fast writes,data loss risk on crash.refresh-aheadRefresh popular keys before theyexpire. Fewer misses, wasted workon keys nobody reads again.negative cachingCache "not found" briefly. Stopsrepeated misses (andcache-penetration attacks) hittingthe DB.
swipe the figure sideways, or tap expand for full screen
1/6
cache-aside
The default. Delete on write (not update) to avoid ordering races between concurrent writers; accept a short staleness window.
the default; delete on writeshort staleness window by design
9

Invalidation, stampedes and Redis

invalidation strategystalenesscost
TTL onlyup to the TTLsimplest; pick TTL per data type
delete on writetiny window (the read-race)every write path must know its keys
versioned keys (user:7:v42)none for readers of the new versionold versions expire unused
CDC-driven invalidationreplication laga consumer of the change stream deletes keys; robust, more moving parts
stampede cures
  1. Request coalescing: one load per key per process (single-flight).
  2. A short lock in Redis (SET key:lock NX PX 3000) so one process across the fleet rebuilds.
  3. Probabilistic early expiry (XFetch): each reader refreshes slightly before expiry with a probability that rises as expiry nears.
  4. Stale-while-revalidate: serve the old value while one refresh runs.

Redis is the workhorse: in-memory data structures (strings, hashes, sorted sets, streams), optional persistence (RDB snapshots, AOF), eviction policies (allkeys-lru, volatile-ttl, allkeys-lfu), and Redis Cluster with 16,384 hash slots. Course 7 covers it in depth. Materialised views are caching inside the database: precomputed results refreshed on a schedule (REFRESH MATERIALIZED VIEW CONCURRENTLY in Postgres needs a unique index).

10

Consistency between cache and database

The question to answer per piece of data is what happens if a user sees a value that is N seconds old. A product description: nothing. A balance shown on a dashboard: confusion, so show its age. A balance used to approve a transfer: money lost, so never cache it. Write the answer down next to the cache key definition.

THE DUAL-WRITE PROBLEM
two systems, two writes, no transaction across them
appdatabasecache / search / KafkaUPDATE balance = 900SET balance:7 = 900
swipe the figure sideways, or tap expand for full screen
1/4
two writes
The app writes the database and then the cache (or search index, or a Kafka topic). There is no transaction spanning both.
two systems, no shared transactioneither write can fail alone