Articles

Cache-Aside vs Read-Through Caching Explained

Cache-aside puts the application in charge of loading cache misses; read-through delegates that job to the cache itself. How each pattern works.

The Lycoris Team The Lycoris Team · · 4 min read
An abstract icon representing caching

Cache-aside and read-through are the two dominant patterns for handling cache misses on reads — and the difference comes down to who’s responsible for loading data from the backing store when the cache doesn’t have it: the application, or the cache itself. Both aim to keep frequently read data in a fast layer like Redis in front of a slower database, but they split the work differently.

Cache-aside: the application drives

In cache-aside (also called lazy loading), the application code owns the whole read path:

  1. Check the cache for the key.
  2. On a hit, return the cached value directly.
  3. On a miss, query the backing store, write the result into the cache, then return it.

The cache is a passive key-value store with no knowledge of where data comes from — it’s just told what to hold. This is the most common pattern in practice because it’s simple to reason about and works with any generic cache: Redis, Memcached, an in-process map. It also means only data that’s actually been requested ever ends up in the cache, which keeps memory usage proportional to real traffic patterns rather than pre-loading things nobody asks for.

The cost is that every service touching this data needs to implement the same miss-then-populate logic, and it’s easy to introduce bugs — forgetting to populate the cache after a miss, or letting a race condition write stale data into the cache when two requests miss on the same key simultaneously.

Read-through: the cache drives

Read-through caching pushes that responsibility into the cache layer itself. The application only ever talks to the cache; the cache is configured with a loader function or plugin that knows how to fetch from the backing store on a miss.

  1. The application asks the cache for a key.
  2. On a hit, the cache returns it directly — identical to cache-aside.
  3. On a miss, the cache itself queries the backing store, stores the result, and returns it to the caller. The application never sees the miss as a separate step.

This centralizes the loading logic in one place instead of duplicating it across every service that reads the data, and it eliminates a class of bugs where an application forgets to populate the cache. The tradeoff is that it requires a caching layer (or library) that actually supports pluggable loaders — not every cache does — and it couples the cache configuration to the data-loading logic, which can make the cache layer feel less like a generic, swappable component.

Side-by-side

Cache-asideRead-through
Who loads on a missApplication codeThe cache itself
Cache knowledge of data sourceNone — generic key-value storeConfigured with a loader
Duplicated logic across servicesYes, unless shared in a libraryNo — centralized in the cache
Works with any cacheYesOnly caches/libraries that support loaders
Common inMost application-level cachingCaching libraries, some managed cache products

What about writes?

Both patterns describe read behavior only — they say nothing about how writes are handled, which is a separate decision covered by write-through, write-back, and write-around caching. It’s entirely normal to pair cache-aside reads with write-around writes, or read-through with write-through — the read and write strategies are independent axes, and picking one doesn’t lock you into a specific choice for the other.

Staleness and invalidation

Neither pattern automatically keeps the cache fresh when the backing store changes outside of a cache-driven write — if another process updates the database directly, the cache can serve stale data until that key’s TTL expires or something explicitly invalidates it. This is true regardless of which pattern you pick, and it’s why most production systems pair either one with a short TTL, an explicit invalidation step on writes, or a change data capture pipeline that pushes invalidations from the source of truth.

Choosing between them

Cache-aside is the practical default for most applications: it works with any cache, it’s easy to debug because the loading logic is visible in application code, and it doesn’t require a specialized caching library. Reach for read-through when the same data-loading logic needs to be shared consistently across many services or call sites and duplicating it in cache-aside form would be error-prone — or when you’re already using a caching library or platform that offers read-through as a built-in feature, since it removes an entire class of forgot-to-populate bugs for free.

Either way, the underlying goal is the same one behind an LRU cache or a CPU’s own multi-level cache hierarchy: keep the hot data close and cheap to access, and only pay the slow-path cost when you genuinely have to.

The takeaway

Cache-aside puts the application in charge of noticing a miss and populating the cache; read-through delegates that job to the cache via a configured loader, so the application only ever sees hits. Cache-aside is simpler to adopt with any generic cache; read-through centralizes the loading logic and removes a common source of bugs when many services share the same cached data. Neither pattern handles write consistency on its own — pair it with the right write strategy and an invalidation plan.

The Lycoris Team The Lycoris Team · · 5 min read

Write-Through vs Write-Back vs Write-Around Caching

Write-through writes to cache and store together, write-back delays the store write, write-around skips the cache on writes entirely. When to use each.

#Databases #Performance #Backend
The Lycoris Team The Lycoris Team · · 5 min read

Thundering Herd Problem (Cache Stampede) Explained

The thundering herd problem hits when a cached value expires and every waiting request floods the backend at once. Here's why it happens and how to stop it.

#Web Development #Performance #Backend