Follow AtlasMart catalog reads and writes through five cache patterns, then compare source-of-truth ownership, stale windows, invalidation, failure ordering, and write-behind loss.

Cache-Aside, Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Patterns

Caching moves work between reads, writes, queues, and derived copies. AtlasMart must make ownership and failure ordering explicit before optimizing latency.

Advanced115–150 minutesCache-pattern labPython 3.13+ · standard libraryVendor-neutral · free/local mandatory pathLast reviewed: August 2026
01

Trace cache hits, misses, invalidation, and writes through cache-aside, read-through, write-through, write-behind, and refresh-ahead patterns.

02

Identify the authoritative system and the ordering point at which a client receives success for each pattern.

03

Diagnose stale windows and the acknowledged-write-loss risk of non-durable write-behind queues.

04

Choose refresh and invalidation behavior from workload evidence instead of treating caching as a transparent speed layer.

1. Every cache pattern is an ownership and ordering contract

AtlasMart's catalog page is read thousands of times more often than it is edited. A cache can remove repeated work, but it adds a second copy of the fact. The important design question is therefore not “which cache API is fastest?” It is who owns the truth and in what order are source, cache, queue, and client acknowledgement updated?

Cache-aside makes the application read cache first and load the authoritative store on miss. Read-through moves that loading behavior behind the cache abstraction. Write-through synchronously updates the authoritative path and cache according to a defined ordering. Write-behind acknowledges before the authoritative store necessarily contains the change. Refresh-ahead proactively reloads items before they expire.

2. Read paths: cache-aside and read-through

In cache-aside, a miss causes source lookup and cache population. If the source changes and invalidation is lost, the cache can return version 1 after the source holds version 2. Read-through does not remove that consistency problem; it changes which component owns miss loading. Both need a freshness contract: TTL, explicit invalidation, version validation, or some combination.

Pattern Miss behavior Authoritative owner Typical risk
Cache-aside application loads source source database/service lost invalidation → stale hit
Read-through cache/provider loads source source remains authoritative unless explicitly designed otherwise provider failure policy can be opaque

3. Write-through versus write-behind

Write-through pays more latency on the write path so the source and cache can be updated before success. That still requires a failure policy if one update succeeds and the other fails. Write-behind can reduce perceived write latency by placing a change in cache or a queue first, but its durability is only as strong as the acknowledged queue. If that queue is volatile and the process crashes, the client can have received success for a fact that never reached the system of record.

Durability boundary

“Cached” is not synonymous with “durable.” A write-behind design needs an explicit durable journal/queue, idempotent drain, ordering rules, retry policy, and recovery procedure if acknowledged data must survive failure.

4. Refresh-ahead can create load instead of removing it

Refreshing every key before expiry sounds safe, but it can spend origin capacity on cold data. AtlasMart has 100 cached catalog keys but only five are read during the next interval. Blindly refreshing all 100 performs 95 unnecessary source reads. A useful refresh-ahead policy needs evidence about access probability, recomputation cost, source headroom, and acceptable staleness.

5. AtlasMart lab: trace ownership and stale windows

Mandatory lab environment

Python 3.13+ standard library only. All outages and lost messages are simulated in memory; no database/cache server or destructive failure injection is required.

python · AtlasMart deterministic simulation
source = {"sku-7": {"price": 100, "version": 1}}
cache = {}

def source_read(k):
    return dict(source[k])

def cache_aside_read(k):
    if k in cache:
        return "hit", dict(cache[k])
    value = source_read(k)
    cache[k] = dict(value)
    return "miss", value

print("CACHE-ASIDE")
print(cache_aside_read("sku-7"))
source["sku-7"] = {"price": 120, "version": 2}
print("source updated to v2; invalidation message is lost")
print("next cache-aside read:", cache_aside_read("sku-7"), "<- stale")
cache.pop("sku-7", None)
print("after invalidation/reload:", cache_aside_read("sku-7"))

print("\nWRITE-THROUGH")
def write_through(k, value):
    source[k] = dict(value)       # authoritative commit first in this teaching policy
    cache[k] = dict(value)
write_through("sku-7", {"price": 130, "version": 3})
print("source/cache:", source["sku-7"], cache["sku-7"])

print("\nWRITE-BEHIND FAILURE")
cache["sku-7"] = {"price": 140, "version": 4}
queued = [("sku-7", dict(cache["sku-7"]))]
print("client acknowledged from cache v4; process crashes before drain")
queued.clear()                   # lost non-durable queue
cache.clear()                    # cache also lost
print("after restart source is still:", source["sku-7"], "<- acknowledged v4 lost")

print("\nREFRESH-AHEAD LOAD")
keys = [f"sku-{i}" for i in range(100)]
actually_read = set(keys[:5])
refresh_all = len(keys)
refresh_only_hot = len(actually_read)
print("blind refreshes:", refresh_all, "useful refreshes:", refresh_only_hot)
print("lesson: every cache pattern has an ownership and failure-ordering contract")
Expected evidence

The first cache-aside read is a miss. After the source moves to version 2 and invalidation is deliberately lost, the next hit remains version 1. Clearing/reloading repairs it. Write-through places version 3 in both copies. The deliberately volatile write-behind queue loses acknowledged version 4 on crash. Blind refresh-ahead performs 100 refreshes for five actually read keys.

6. Deliberately wrong approach: “write the cache, then eventually fix the database”

If product price, payment status, or inventory ownership is acknowledged only in an evictable cache, eviction/failover can erase authoritative business state. If write-behind is truly needed, the durable queue becomes part of the system of record and must be backed up, monitored, replayable, tenant-scoped, and tested under retries and reordering.

7. Production judgment and bridge

Choose a pattern from source-of-truth ownership, read/write ratio, tolerance for stale reads, reconstruction cost, failure ordering, and tail-latency budget. Measure hit ratio by endpoint/key class, stale-hit incidents, invalidation lag, queue age/depth, refresh work, origin QPS saved, and origin QPS created by refresh. Avoid putting secrets or cross-tenant responses into insufficiently scoped keys.

The next lesson assumes the cache is bounded and asks a different question: when memory is full, which entries should be evicted?

Check your understanding

  1. Does read-through make the cache authoritative?
  2. What is the key failure mode in volatile write-behind?
  3. Why can cache-aside return stale data after a source update?
  4. Why can refresh-ahead increase origin load?
  5. What must be monitored in durable write-behind?
Review the answers

1. No. It only changes who performs miss loading unless the architecture explicitly assigns ownership to the cache.

2. A client can receive success before the source is durable; loss of the queue/cache can erase the acknowledged write.

3. An invalidation may be delayed/lost or TTL may not yet have elapsed.

4. It can refresh cold keys that no caller would have requested.

5. Queue durability, age/depth, retries, ordering, idempotency, and the lag between acknowledgement and authoritative persistence.

References

Foundational claims use primary research/specifications where practical. Redis is an optional current implementation example; no product installation is required for the mandatory labs.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.