Module F-12·22 min read

The four caching strategies with their consistency guarantees, failure modes, and implementation trade-offs. Cache stampede prevention, invalidation timing, cache warm-up, and the decision framework for choosing the right pattern.

JJS
Written by Jatin Jain Saraf · Senior Software Engineer

F-12 — Caching Patterns: Cache-Aside, Write-Through, Write-Behind, and Read-Through

Who this module is for: You use Redis as a cache — you call redis.get(), fall back to the database on miss, and call redis.set(). That is the cache-aside pattern, and it is correct for many situations. But it is not the only pattern, and it is not always the right one. This module covers the four caching strategies with their trade-offs, consistency implications, and the failure modes most tutorials skip.


What a Cache Actually Does

A cache sits between your application and your primary data store (typically a database). Its job is to answer repeated, expensive lookups cheaply by storing previously computed or fetched results in fast memory.

Every caching strategy makes a trade-off across three dimensions:

  1. Consistency — how closely the cache matches the source of truth at any given moment
  2. Complexity — how much application code is required to maintain the cache
  3. Failure behavior — what happens when the cache is empty, stale, or unavailable

Pattern 1: Cache-Aside (Lazy Loading)

Also called "lazy loading" or "look-aside cache." This is what most engineers mean when they say "we cached it in Redis."

How It Works

text

Read path:

  1. Try GET key from Redis
  2. On hit: return the cached value
  3. On miss: query the database, store the result in Redis with a TTL, return the value

Write path: Write directly to the database. Optionally invalidate (delete) the cache key. Do not write to the cache on writes.

typescript

When to Use Cache-Aside

  • Read-heavy workloads where most reads are for data that rarely changes
  • Resilient to cache failures — if Redis goes down, the application still works (just slower)
  • Infrequent writes — cache invalidation on every write is cheap

Trade-offs and Failure Modes

Cache stampede (thundering herd): If a popular key expires, many concurrent requests simultaneously see a miss and all rush to the database. The database receives N copies of the same expensive query. Solutions:

  • Probabilistic early expiry (PER): Recompute when TTL < threshold × random(). Some requests recompute before the key expires, warming the cache before the stampede.
  • Mutex / distributed lock: The first miss acquires a lock, computes, and populates the cache. Others wait and then read from the cache.
typescript

Stale data: After an invalidation (DEL), the next request fetches fresh data from the database. But if you forget to invalidate after a write, the cache serves stale data until TTL expires.

Cache miss on cold start: After a deployment or Redis restart, the cache is empty. All requests hit the database simultaneously. Mitigate with cache warming (pre-populate common keys on startup).


Pattern 2: Write-Through

Write-through keeps the cache synchronously updated on every write. The application writes to the cache and the database together — the write is only acknowledged when both succeed.

How It Works

text

Read path: Same as cache-aside — try cache, fall back to database.

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.