LearnastraTHE DESIGN COURSE

Make the right trade-off / CHAPTER 47

Cache-aside, read-through, write-through, and write-back

Show who loads missing copies, how edits race with refills, and where accepted buffered writes must survive a crash.

Advanced1h of deliberate study10 questions

Builds on Caching fundamentals, Client-side versus server-side caching, Strong versus eventual consistency.

By the end, you can
  • Trace reads and writes for four common cache patterns.
  • Explain why invalidation after a database write can still race.
  • Protect acknowledged write-back data from loss.

01Who owns the miss path?

An application reads product 42. The database stores its current record and a cache may hold a reusable copy. The pattern name first tells us who handles a missing copy:

  • Cache-aside: application code checks the cache, reads the database on a miss and saves the copy.
  • Read-through: the application asks a cache library or service; that component performs the miss load behind its interface.

Both can avoid caching unused records, but a miss is slower and many simultaneous misses can overload the source. Neither read pattern defines how writes remain current.

Two write patterns move responsibility differently:

  • Write-through: the application waits for the source write and normally refreshes the cache through the same write interface. The two stores can fail separately; state exactly what success promises and how a failed cache update affects reads.
  • Write-back: an intermediate buffer accepts the change first and writes the source later. The buffer now holds acknowledged changes the source may not contain; those changes are not disposable cache copies.

02Two copies do not update atomically by magic

Trace the normal read before considering failures

  1. The application requests product 42's cached value.
  2. On a miss, the application or read-through loader reads the source record.
  3. It checks the returned version against any minimum requested by the caller and records when the source read began. It returns and caches the value only if that version and the allowed age meet the read’s rule; time spent waiting to fill the cache cannot restart its age.
  4. Other callers needing the same value can share that in-progress load, called coalescing. Do not combine requests whose private data or required versions differ.

Now state the write promises

Pattern Who performs the work? What must the explanation include?
Cache-aside Application code loads misses. Which source/version may be cached and how edits invalidate or update it.
Read-through Cache library/service loads misses. Its loading and failure policy; writes still need their own rule.
Write-through A defined path writes the source and attempts the corresponding cache update. Actual ordering, partial-failure result, retries and stale-reader behavior.
Write-back A buffer saves the accepted change; the source follows later. What durable copies the buffer has, update order, finite capacity and recovery.

Two independent stores do not become one transaction because the pattern has “through” in its name. If the source commits but the cache change fails, explain what the caller receives, how readers avoid unsafe old data and how repair is retried without repeating the business effect.

Write-back: acknowledgment makes the buffer a durability owner

Write-back: acknowledgment makes the buffer a durability ownerFor a one-machine-loss promise, save the accepted change on independent durable buffer copies before success. Reply only after those copies are saved; the source database may still lack the change. Send each product change with its request ID and version. After restart, replay pending IDs; the source skips IDs already applied and does not replace a newer product version with an older one. Numbered arrows correspond to the connection key below. All connections are directional.ApplicationReuses one writerequest identityDurable writebufferSave accepted changesbefore successSource databaseReceives the bufferedchanges later123Write-back: acknowledgment makes the buffer a durability ownerFor a one-machine-loss promise, save the accepted change on independent durable buffer copies before success. Reply only after those copies are saved; the source database may still lack the change. Send each product change with its request ID and version. After restart, replay pending IDs; the source skips IDs already applied and does not replace a newer product version with an older one. Numbered arrows correspond to the connection key below. All connections are directional.ApplicationReuses one writerequest identityDurable writebufferSave accepted changesbefore successSource databaseReceives the bufferedchanges later123Write-back: acknowledgment makes the buffer a durability ownerFor a one-machine-loss promise, save the accepted change on independent durable buffer copies before success. Reply only after those copies are saved; the source database may still lack the change. Send each product change with its request ID and version. After restart, replay pending IDs; the source skips IDs already applied and does not replace a newer product version with an older one. Numbered arrows correspond to the connection key below. All connections are directional.ApplicationReuses one writerequest identityDurable writebufferSave accepted changesbefore successSource databaseReceives the bufferedchanges later123

Follow the numbered arrows

  1. Application → Durable write buffersubmit the change and its request ID
  2. Durable write buffer → Applicationreply after the promised durable save
  3. Durable write buffer → Source databasesend changes in order with repeat checks
The source does not yet contain these accepted changes. The write-back buffer must retain them through the failures promised to callers.
Trace the diagram in words
  1. For a one-machine-loss promise, save the accepted change on independent durable buffer copies before success.
  2. Reply only after those copies are saved; the source database may still lack the change.
  3. Send each product change with its request ID and version. After restart, replay pending IDs; the source skips IDs already applied and does not replace a newer product version with an older one.

03Worked example: stale refill and write-back loss

One illustrative write-through path

Here product descriptions may be 30 seconds old, but a successful edit promises the new database value is durable—not that every copy changed atomically.

  1. The application receives the new description and request key edit-8. In one database transaction it saves product version 8, the original request/result and pending cache-refresh work.
  2. In one cache operation, the application compares version 8 with the saved value and retained minimum version. It stores 8 only if neither requires a newer version, and raises the minimum to at least 8. If a newer edit has already stored 9, it keeps 9 rather than replacing it with 8. It then marks this refresh work finished.
  3. If the database committed but the cache update failed, attempt to remove the old copy and leave the refresh work pending for retry. Return the saved database outcome with the cache-refresh state, not a claim that both stores committed together. A repeated edit-8 retrieves that result instead of editing again.
  4. Reads needing version 8, including the editor's immediate confirmation, compare the cache’s recorded version with 8 and go to the database if it is lower or absent. Ordinary description reads may use an older copy only within the stated 30-second limit; required current permissions or stock never rely on that copy. A failed invalidation cannot extend its lifetime.

Now trace a delayed cache-aside fill

The source has product version 7. Follow a delayed fill:

  1. Reader R misses, reads version 7 and pauses before writing the cache.
  2. Writer W commits version 8 in the database.
  3. W deletes the cached value after commit. This avoids deleting before the database change, but does not cancel R's earlier read.
  4. R resumes and puts 7 into the now-empty cache. A new reader can receive old data despite database-first invalidation.
  5. To reject that fill, retain a minimum acceptable version, such as 8, even when the value is absent. In one indivisible cache operation, compare an incoming fill with this marker and reject versions below it. Updates must maintain the marker too; deleting all evidence of 8 loses that protection.

That retained marker is sometimes called a tombstone or version watermark. Keep it while old delayed or replayed fills can still return. If it expires too early, the same race can reappear. Without an enforced limit on how long old fill attempts may return, no finite marker-expiry time proves they are all gone. A short TTL permits some old reads before expiry; a delayed second delete only narrows a timing window under assumptions.

This fill protection alone does not prove immediate permission revocation. Every protected read must also check current permission or participate in a coordinated rule covering cached reads, not just fills.

Now consider write-back. Five hundred accepted changes have not reached the source. If they exist only in failed volatile memory, they are lost. For a one-machine-loss promise, save each accepted change on independent durable buffer copies before replying. Keep its request ID, product version and pending status. A sender later applies it to the source using that ID/version; the source skips an already-applied ID and rejects an older version. Remove pending work only after source success. A crash before that removal causes a repeat, which those saved checks resolve. Naming the buffer a cache does not preserve missing bytes.

Cache-aside: the application owns miss loading

Cache-aside: the application owns miss loadingThe application asks for a reusable copy of product 42. On a miss, it reads the database and compares the returned version with any minimum version requested by the caller. Before saving a fill, enforce the retained minimum-version comparison; deleting a value did not cancel older readers. Numbered arrows correspond to the connection key below. All connections are directional.ApplicationRequests a permittedproduct 42 readReplaceable cacheSaved value and itssource versionProduct databaseStores the currentproduct versionCheck the fillversionKeep the minimumallowed sourceversion1234Cache-aside: the application owns miss loadingThe application asks for a reusable copy of product 42. On a miss, it reads the database and compares the returned version with any minimum version requested by the caller. Before saving a fill, enforce the retained minimum-version comparison; deleting a value did not cancel older readers. Numbered arrows correspond to the connection key below. All connections are directional.ApplicationRequests a permittedproduct 42 readReplaceable cacheSaved value and itssource versionProduct databaseStores the currentproduct versionCheck the fillversionKeep the minimumallowed sourceversion1234Cache-aside: the application owns miss loadingThe application asks for a reusable copy of product 42. On a miss, it reads the database and compares the returned version with any minimum version requested by the caller. Before saving a fill, enforce the retained minimum-version comparison; deleting a value did not cancel older readers. Numbered arrows correspond to the connection key below. All connections are directional.ApplicationRequests a permittedproduct 42 readReplaceable cacheSaved value and itssource versionProduct databaseStores the currentproduct versionCheck the fillversionKeep the minimumallowed sourceversion1234

Follow the numbered arrows

  1. Application → Replaceable cachelook up a reusable product copy
  2. Application → Product databaseread the database on a miss
  3. Application → Check the fill versionsubmit the loaded version for comparison
  4. Check the fill version → Replaceable cachecompare and save in one cache operation
The application loads a missing product. A delayed old fill needs a version check; write-back separately makes its buffer responsible for accepted changes.
Trace the diagram in words
  1. The application asks for a reusable copy of product 42.
  2. On a miss, it reads the database and compares the returned version with any minimum version requested by the caller.
  3. Before saving a fill, enforce the retained minimum-version comparison; deleting a value did not cancel older readers.

04Select a protocol, not only a pattern name

Choose a complete protocol, not just a pattern name:

  • Replaceable reads: cache-aside or read-through can fit when reuse saves work and allowed age is explicit. Choose where loading logic is easiest to maintain and observe.
  • Strict permissions or exclusive stock: use current enforced decisions unless a coordinated cache design demonstrably supplies the same guarantee.
  • Write-through: show source/cache ordering, safe request retries, invalidation or repair, and what partial success means.
  • Write-back: require durable accepted changes, required order, capacity limits and restart recovery. For a display title, version 9 can replace a still-buffered version 8 when only the latest title matters. Do not discard separate payments or audit events that must each be recorded.
  • Load protection: limit simultaneous fills and vary expiry times to avoid synchronized reloads.

Trace cache failure, source failure, a paused reader, a crash after an effect and restart. Separate the ordinary read-cache diagram from the write-back diagram so it is clear when a buffer becomes responsible for data the source does not yet have.

DRAW. CALCULATE. EXPLAIN.

Close the permission-refill race

20 minutes · guided practice

Reader R loads permission version 7 and pauses. An admin commits revocation 8 and invalidates. R then tries to fill 7. Immediate revocation is required. Propose a correct decision path and explain whether acknowledging revocation in a volatile write-back cache is sufficient.

Your self-check
Compare the worked solution
  1. Deleting the value does not cancel R's version 7 read. R can still restore 7 after revocation 8.
  2. For immediate revocation, check current permission on every protected action, or demonstrate a coordinated version rule covering every read and fill. Protecting fills alone is insufficient.
  3. If using a minimum-version marker, update and compare it indivisibly and retain it while old work can return. TTL or a second delayed deletion is not proof of immediate revocation.
  4. A volatile write-back buffer cannot promise crash-surviving revocation. Save the accepted revocation durably and ensure it is enforced before reporting success.

The checkboxes record your own assessment. The worked solution gives you a way to check the reasoning.

RETRIEVE BEFORE YOU REVEAL

Keep the important ideas.

Say the answer aloud, then turn the card over.

CARD 01Who loads cache-aside and read-through misses?
  1. Cache-aside: application code loads the source and fills the cache.
  2. Read-through: the cache library/service performs that load.
  3. Both need separate rules for writes, old fills and failures.
CARD 02How can a deleted cache entry become stale again?
  1. A reader loads 7 and pauses.
  2. The writer saves 8 and deletes the cache.
  3. The reader restores 7.

Keep a minimum allowed version; compare and reject older fills in one cache operation.

CARD 03What must write-back preserve?
  1. Save acknowledged changes on the independent durable copies needed for promised failures.
  2. Preserve per-record versions so older writes cannot replace newer ones.
  3. Replay pending IDs after restart; the source skips IDs already applied.

The source cannot recover changes that existed only in failed buffer memory.

Practise this topic in the revision deck ↗

CHECK THE REASONING

Can you explain the choice?

Answer every question, then finish for an immediate score and an explanation for each answer. Aim for 80%, then revisit any gaps.

1 / 10The application checks cache, loads source on miss and populates. Which pattern is described?
2 / 10What changes when the same loading logic is behind a cache abstraction?
3 / 10R loaded 7; W commits 8 and invalidates; R then fills 7. What occurred?
4 / 10Why retain a minimum-version marker when removing a value?
5 / 10What if the version watermark expires before a delayed old fill returns?
6 / 10500 acknowledged writes exist only in failed cache memory. What is exposed?
7 / 10What can coalescing compatible misses save?
8 / 10Does a short TTL prove immediate permission revocation?
9 / 10Source write commits but cache update fails in a write-through path. What must be defined?
10 / 10Why can write-back reduce source work?
0 of 10 answered

CLOSE THE LOOP

Explain it without the page.

Summarise the mechanism, one trade-off, and one failure mode. Then mark this chapter complete.

Go to the source