🗓️ 29042026 1915
📎 #caching #patterns

READ THROUGH WRITE THROUGH WRITE BACK

The three patterns where the cache itself manages persistence — alternatives to cache_aside for cases where the application shouldn't (or can't) drive cache logic explicitly.

The Three Patterns​

ABSTRACT

Read-through — on miss, the cache fetches from the source, populates itself, and returns. Write-through — application writes to the cache; the cache writes synchronously to the source before returning. Write-back / write-behind — application writes to the cache; the cache asynchronously batches writes to the source.

In all three, the cache participates in the path. Compare to cache_aside where the cache is "on the side" and the app drives invalidation.

Read-through​

read(key):
return cache.get(key)
# cache library handles miss → fetch → populate transparently

The application sees a single API: cache.get(key). The cache library knows how to load on miss (configured via a loader function or DAO).

Pros: app code is simpler. Cache and source are tightly integrated.

Cons: hides failures (a slow source slows every miss). Couples app to a specific cache library's loader API. Hard to swap caches.

Used in: Caffeine (Java) LoadingCache, Guava cache, AWS ElastiCache with read-through, Spring @Cacheable.

Write-through​

write(key, v):
cache.set(key, v) # cache writes to source synchronously
# source write happens before cache returns

Every write goes through the cache and waits for the source to confirm. The cache is always consistent with the source.

Pros: no read/write race; reads after writes always see the new value. Stronger consistency than cache-aside.

Cons: writes are slow (cache RTT + source RTT). Requires a write path through the cache, which not all systems support (Redis doesn't natively; you build it on top).

Used in: write-through caches in DBs (InnoDB buffer pool with O_SYNC-like semantics), some CDN edge caches, Memcached + custom write-through wrapper.

Write-back (Write-behind)​

write(key, v):
cache.set(key, v) # returns immediately
# cache asynchronously batches writes to source later

The cache acks the write before the source has the value. The source catches up on a timer or batch threshold.

Pros: very fast writes. Coalesces multiple writes to the same key (only the last one hits the source). Buffers spikes.

Cons: the source lags the cache — readers querying the source directly see stale data. Cache crash before flush = data loss. Requires durable cache or replication for any safety.

Used in: CPU caches (the original write-back), database buffer pools, log-structured merge trees, click counters, view counters where loss is tolerable.

Comparison Table​

PatternApp writes toSource updatedRead-after-writeFailure mode
Cache-asideSourceAlwaysEventual (after invalidate)Stale data if invalidate missed
Read-throughSourceAlwaysSame as cache-asideSlower miss path (hidden)
Write-throughCacheSynchronouslyImmediate (consistent)Slow writes
Write-backCacheAsynchronouslyImmediate (cache); lagged (source)Data loss on cache crash

Choosing​

  • Cache-aside for typical web apps. App-driven, robust, well-understood.
  • Read-through when you want to hide caching from business code (e.g. Spring @Cacheable).
  • Write-through for stronger consistency on writes when latency budget allows.
  • Write-back for write-heavy ephemeral data (counters, logs, telemetry) where loss windows are acceptable.

Common Pitfalls​

  • Write-back without durability is dangerous — a crash loses every unflushed write. Use replicated cache or persist-on-write to local disk before acking.
  • Mixing patterns — different code paths using different patterns for the same key produces confused invalidation. Pick one per cache.
  • Read-through hides slow source — your "cache hit rate" looks fine but tail latency on misses is hidden inside the cache layer. Monitor source latency separately.
  • Write-through on a slow source — cache write throughput becomes source write throughput. If your source can do 1k writes/sec, neither can your cache.
  • Eviction with write-back — if the cache evicts a dirty (unflushed) entry, you lose the write. Pin or block eviction of dirty entries; flush before eviction.
  • cache_aside — the more common alternative.
  • cache_stampede_thundering_herd — read-through with naive miss handling can stampede.
  • redis_cluster — write-through to a clustered Redis is non-trivial under partition.
  • outbox_pattern — for write-back-like behaviour with durability guarantees.

References​