System designby Learnastra

Concept lesson · Foundations

Caching: cache hits, misses, write policies and invalidation

By Anup Rai

Start here

Definition

Caching stores a reusable copy of data or a computed result so later requests can avoid repeating a more expensive operation. A cache hit uses an acceptable cached entry; a cache miss must obtain the result from another source.

Why it matters: Many users ask for the same product, image or calculation. Reusing a valid result reduces latency and work at the authoritative source, but creates a freshness problem when that source changes.

The visual modelCache invalidation and the stale-fill race

A read may fetch price version 8 before a writer commits version 9, then populate its old result after invalidation. Check the version atomically when inserting the cached value.

Cache invalidation and the stale-fill raceA read may fetch price version 8 before a writer commits version 9, then populate its old result after invalidation. Check the version atomically when inserting the cached value. Product P7 starts at price $20, version 8. A reader misses and begins fetching that old version. A writer commits a new price at version 9 and invalidates the cache. The delayed reader attempts to fill version 8 after invalidation. Without a guard it resurrects stale data. One solution retains an invalidation fence 9 and atomically compares that fence with insertion, rejecting fills with older versions. Expiry alone does not close a stale-refill race.P7: a delayed read can refill old price dataReaderCacheDatabase1. miss2. read v8 ($20); delay fill3. writer commits v94. invalidate; fence = v95. delayed fill v8Cache: atomically reject 8 < fence 9Without this check: stale v8 returnsThe write and fence need a coherent protocol. TTL alone does not close the stale-fill race.
Read the diagram step by step
  1. Product P7 starts at price $20, version 8. A reader misses and begins fetching that old version.
  2. A writer commits a new price at version 9 and invalidates the cache.
  3. The delayed reader attempts to fill version 8 after invalidation. Without a guard it resurrects stale data.
  4. One solution retains an invalidation fence 9 and atomically compares that fence with insertion, rejecting fills with older versions. Expiry alone does not close a stale-refill race.

Worked example

The database holds P7 at $20/version 8. Request R1 misses and fills the cache; request R2 hits that copy. When the seller commits $25/version 9, the old copy needs an explicit invalidation or freshness rule.

Key takeaways

  • Identify the authoritative source and every cache-key input.
  • Expiration, invalidation and eviction solve different problems.
  • A cache failure can expose the full request rate to the origin.

You will learn to

  • Trace a cache hit, miss, and concurrent stale refill.
  • Choose a write strategy and an acceptable freshness rule.
  • Replay eviction policies and protect the origin during a cache outage.

Practice in this chapter

8 interview questions with model answers and follow-ups.

Go to interview practice

Useful foundations: Load balancing: definition, algorithms and failover · Databases, data models, and ACID transactions

Workload and timing examples are interview assumptions.

01Caching: definition, hits, misses and TTL

Caching stores a reusable copy of data or a computed result so later requests can avoid a slower or more expensive operation. A cache entry is addressed by a cache key, such as product:P7. The authoritative store holds the record the application treats as the source of truth, such as the product database. A cache holds a copy. Its freshness policy states how old that copy may be, and its access policy states who may read it.

A hit means the cache has an acceptable entry. A miss means the entry is absent or cannot be used. A time to live, or TTL, is how long an entry may remain usable under the cache policy. A TTL is not the same as a guarantee that the underlying value cannot change.

Suppose P7 costs $20 and thousands of people view it each minute. Reusing a small product record can reduce database work. During checkout, however, the service must check which price applies and whether stock is available under the agreed purchase rules. The displayed cached value is not automatically permission to charge an old price or sell unavailable stock.

02Cache placement: local, shared, distributed and CDN

Memory (RAM) is fast temporary working storage; a disk retains bytes with a different access cost. An application can cache in its own memory, avoiding a network call. It can also cache on local disk: slower than RAM, but useful for larger reusable files. With several application servers, these local caches are separate. If the next request reaches another server, that server may miss or hold a different version.

A shared cache gives applications a common network-accessible cache. A distributed cache spreads that cache's keys across several machines. “Shared” describes who can use it; “distributed” describes how its capacity is placed. Neither word specifies durability or the freshness protocol.

A browser cache stores a user's copy. A reverse-proxy cache serves requests in front of the application. A content delivery network, or CDN, keeps copies at edge locations nearer users. For a product image, the first edge request misses and fetches the object from the origin, the server or storage service that supplies the original content; later allowed requests reuse it.

A separate hostname for static content, such as static.shop.example, makes a later CDN migration easier. Initially it serves image files directly. Later that hostname can point through a CDN while the object paths remain stable. Cache keys, certificates, cache headers, and private-content policy still need configuration; changing DNS alone does not define correct caching.

Placement Benefit Cost or limit
In-process memory No cache-network round trip Each application has a separate copy; restart loses it
Local disk More reusable bytes per host Slower access than RAM and still host-specific
Shared network cache Many applications reuse the same entries Network dependency and another service to operate
CDN edge cache Repeated permitted bytes are served nearer users Cache-key, authorization and freshness rules must be correct at the edge

03Worked example: cache-aside read of P7

Cache-aside means the application manages cache lookup and refill. Start with a database record {id:P7, price:20, version:8} and an empty cache.

Concept in focusCache-aside: miss first, hit later

Cache-aside puts lookup and refill in the application. A hit is acceptable only under the cache's freshness and authorization rules.

Cache-aside: miss first, hit laterCache-aside puts lookup and refill in the application. A hit is acceptable only under the cache's freshness and authorization rules. Application to Cache: GET product:P7 Cache to Application: MISS Application to Database: Read P7 Database to Application: price = 20; version = 8 Application to Cache: SET P7, version 8, with expiry Application to Cache: Later request: GET P7 Cache to Application: HIT: return version 8ApplicationCacheDatabaseGET product:P7MISSRead P7price = 20; version = 8SET P7, version 8, with expiryLater request: GET P7HIT: return version 8

Remember: Miss -> load -> fill; hit -> reuse.

Read the diagram
  1. Application to Cache: GET product:P7
  2. Cache to Application: MISS
  3. Application to Database: Read P7
  4. Database to Application: price = 20; version = 8
  5. Application to Cache: SET P7, version 8, with expiry
  6. Application to Cache: Later request: GET P7
  7. Cache to Application: HIT: return version 8
  1. Request R1 reads P7. The application checks key product:P7 and misses.
  2. It reads version 8 from the database, stores a copy with a 30-second TTL, and returns $20.
  3. Request R2 reads P7 one second later. The same cache key hits; no database read is needed for that product-page request.
  4. When the TTL expires, the next request needs a refresh. The cached copy did not update itself when time passed.
Worked example diagramFirst read: application misses, reads the database and fills the cache. Second read: the copy satisfies the request without another product database lookup. A later price change needs the invalidation protocol explained below.
Caching: cache hits, misses, write policies and invalidation: architecture diagram1. R1: first read P7 to 2. Application: 1. Request product P7; 2. Application to 3. Cache: empty, then P7 v8 / $20: 2. Look up key: first read misses; 2. Application to 4. Database: P7 v8 / $20: 3. On miss, read authoritative version 8; 2. Application to 3. Cache: empty, then P7 v8 / $20: 4. Store copy with expiry; 5. R2: second read P7 to 2. Application: 5. Second request for P7; 3. Cache: empty, then P7 v8 / $20 to 2. Application: 6. Hit: return acceptable cached copy1 → 2: 1. Request product P72 → 3: 2. Look up key: first read misses2 → 4: 3. On miss, read authoritative version 82 → 3: 4. Store copy with expiry5 → 2: 5. Second request for P73 → 2: 6. Hit: return acceptable cached copy01R1: first read P702Application03Cache: empty, thenP7 v8 / $2004Database: P7 v8 /$2005R2: second read P7
  1. 1 → 21. Request product P7R1: first read P7 → Application
  2. 2 → 32. Look up key: first read missesApplication → Cache: empty, then P7 v8 / $20
  3. 2 → 43. On miss, read authoritative version 8Application → Database: P7 v8 / $20
  4. 2 → 34. Store copy with expiryApplication → Cache: empty, then P7 v8 / $20
  5. 5 → 25. Second request for P7R2: second read P7 → Application
  6. 3 → 26. Hit: return acceptable cached copyCache: empty, then P7 v8 / $20 → Application

04Cache read and write policies

Cache placement answers where a copy lives. A cache policy answers who loads a missing copy and how a write reaches durable storage and existing cached copies. These are separate decisions: invalidation marks or removes a cached value so later readers cannot keep using it as current. For P7, the policy must explain what happens both when a page read misses and when the seller changes the price.

Pattern What happens on a write or miss What you must handle
Cache-aside Application reads database on a miss Refill races and invalidation
Read-through Cache layer loads missing data Loader failures and source access
Write-through Cache layer writes through to durable storage before success Partial failure across the two stores
Write-around Write database directly, invalidate old cached value Next read misses; avoid stale leftovers
Write-back Acknowledge cache write, persist later Loss of acknowledged data unless protected

Read-through describes loading reads; it is not itself a write policy. Write-through can make the write path more explicit, but two independent stores are not automatically one atomic transaction. If the database commits a $25 price while the cache update fails, readers need invalidation, version checks, or a declared staleness limit.

For the public product page, use cache-aside: set a TTL and invalidate the cached price after a database update commits. Accept brief display delays. Checkout must check current price and stock in the purchase transaction. Write-back may suit disposable counters, but important orders need a way to survive cache failure before the service reports success.

05Cache invalidation and the stale-refill race

The seller changes P7 from $20 to $25. Simply deleting the cache after the database write can still race with an earlier reader:

Time Reader R Writer W
t1 Cache miss; reads database version 8 ($20)
t2 Pauses before filling cache Commits version 9 ($25)
t3 Still paused Deletes cached P7
t4 Fills cache with old version 8 Write is complete
Concept in focusStale refill: deletion alone does not close the race

The late refill happens after invalidation. Versions, guarded cache updates or a bounded-staleness policy are additional design choices.

Stale refill: deletion alone does not close the raceThe late refill happens after invalidation. Versions, guarded cache updates or a bounded-staleness policy are additional design choices. Reader to Reader: Read database version 8, then pause. Writer to Writer: Commit version 9. Writer to Cache: Delete the cached entry. Reader to Cache: Resume and refill with old version 8. Cache to Reader: A later hit can now return stale data.ReaderWriterCacheRead database version 8, then pause.Commit version 9.Delete the cached entry.Resume and refill with old version 8.A later hit can now return stale data.

Remember: An old reader can refill after a new writer deletes.

Read the diagram
  1. Reader to Reader: Read database version 8, then pause.
  2. Writer to Writer: Commit version 9.
  3. Writer to Cache: Delete the cached entry.
  4. Reader to Cache: Resume and refill with old version 8.
  5. Cache to Reader: A later hit can now return stale data.

If the business accepts up to a stated stale-display interval, a TTL may be sufficient under a specified refresh policy. If deletion or permission revocation must be immediate, verify current authorization rather than treating a stale cached record as truth. The interview answer should first state how stale a read may be and how quickly a permission change must take effect, then choose a mechanism that meets those requirements.

Suppose the database confirms version 8 at 10:00:00 with permission to reuse it until 10:00:30. A reader receiving it at 10:00:20 has only ten seconds left. Starting a fresh 30-second timer would incorrectly extend use to 10:00:50. Allow for clock differences. After expiry, obtain a newly validated value or return an error if validation fails. This limits age under the stated clock and database assumptions; it still allows stale reads before expiry and does not revoke access immediately.

Recovery also needs a way to distinguish fills started before a cache restart from fills started afterward. A cache generation is an identifier for one such cache lifetime. A refill carries the generation it started in; after recovery selects a new generation, the cache rejects results from the old one even if their requests finally resume.

06Eviction policies: FIFO, LRU, LFU and alternatives

Invalidation removes data because it is no longer acceptable. Eviction removes data because the cache needs space. A perfectly fresh entry can be evicted.

Concept in focusThe same access history evicts different keys

Each row orders entries from next to evict on the left to last to evict on the right.

The same access history evicts different keysEach row orders entries from next to evict on the left to last to evict on the right. Compare FIFO and LRU after the same insert/read sequence. Capacity is three. Insert A, B, C, read A, then insert D. FIFO evicts A, leaving B, C, D. LRU evicts B, leaving C, A, D.Capacity = 3. Insert A, B, C; read A; then insert D.Order before inserting DAfter inserting DFIFOABCBCDevicts ALRUBCACADevicts BLeftmost = next to evict. Reading A changes recency, not arrival order.

Remember: Reading A saves it under LRU; it does not save it under FIFO.

Read the diagram
  1. Compare FIFO and LRU after the same insert/read sequence.
  2. Capacity is three. Insert A, B, C, read A, then insert D.
  3. FIFO evicts A, leaving B, C, D. LRU evicts B, leaving C, A, D.
Try from memoryWhich key survives because it was read recently?

A survives under LRU. FIFO ignores that read when deciding which entry arrived first.

Compare the policies on one trace

Take a two-entry cache: insert A, insert B, read A, then insert C. Before C arrives, insertion order is A then B; access recency is B then A.

Policy What it tracks Victim in this trace
FIFO: first in, first out Insertion order A
LIFO: last in, first out Insertion order, selecting among existing entries before insertion B
LRU: least recently used Access recency B
MRU: most recently used Access recency A

State the insertion convention when illustrating LIFO/MRU.

Other policy Selection rule Important qualification
LFU: least frequently used Least popular over tracked history; with A read twice and B once, B loses Age popularity so old activity does not dominate forever
Random Select without recency or frequency bookkeeping Does not deliberately preserve popular or recent entries

Real implementations may approximate these policies to save CPU and memory. An LRU cache can perform poorly during a large one-time scan because scan entries evict frequently reused data.

Implementation choice and data that must not be evicted

For a disposable shared product cache, one practical option is Redis with an explicit maxmemory limit and a measured choice between allkeys-lru and allkeys-lfu. Redis approximates these policies; LFU also decays old popularity. A volatile-only policy considers only expiring keys, so it is a different capacity policy. Keep durable business records and correctness metadata out of an indiscriminately evictable cache. See the Redis eviction reference; the application still owns freshness and origin-overload protection.

07Cache stampedes, negative caching and outages

When a popular key expires, 10,000 simultaneous readers may all miss and query the database. This is a stampede.

Technique What it changes Boundary
Request coalescing One refresh runs while other callers wait or use an allowed stale copy The waiting/stale behavior must fit the request contract
Randomized TTLs Spreads expiry times across different keys It does not by itself combine requests for one expired key
Stale-while-revalidate Serves an acceptable stale value while refreshing Use only when the freshness contract permits it

Negative caching stores a short-lived “not found” result to reduce repeated lookups of missing keys. It needs a short enough lifetime to let newly created records become visible, and must not reveal to an unauthorized user whether a private record exists.

08HTTP cache control and conditional revalidation

HTTP caches use response directives and validators to make reuse decisions. These are distinct from an application cache’s own TTL configuration.

A directive is an instruction carried in response headers, usually Cache-Control, about whether and how caches may reuse the response. A validator, such as an ETag, identifies a representation version. Revalidation asks the origin whether that cached version is still usable, which can avoid sending the full body again.

Response directive Meaning for reuse Typical purpose
max-age=30 Freshness lifetime is 30 seconds, accounting for response age Briefly reusable content
s-maxage=60 Shared-cache freshness override A separate CDN/proxy lifetime
no-cache Store if otherwise allowed, but validate before reuse Reuse bytes only after checking
no-store Do not store this exchange Sensitive responses
private Shared caches must not store the response User-specific content

For example, an origin returns ETag: "v8". On revalidation the cache sends If-None-Match: "v8"; a 304 response confirms that its selected representation can be reused without resending the body. Vary identifies request headers that select different representations, such as language. It does not perform authorization. Cache directives do not recall bytes already downloaded or replace access checks. See RFC 9111.

Practise the interview questions

Say your answer aloud before opening the model answer. Then answer the follow-up and compare the reasoning.

Foundation · Question 1

What is caching? Use product P7 at $20/version 8 to explain the first miss and a subsequent hit.

Reveal a model answer

“Caching keeps a reusable copy to avoid repeating a more expensive operation. Request R1 for product:P7 misses, so the application loads $20/version 8 from the database and stores a copy. The next permitted request R2 hits that copy. The database remains authoritative; the hit is usable only under the page’s freshness and access policy.”

What the answer must demonstrate: Name the source of truth.

Foundation · Question 2

When would you choose a local cache rather than a shared one?

Reveal a model answer

“A local memory cache is fast and avoids a network dependency; a local disk cache can hold larger reusable objects. But copies differ across application instances and vanish or become unavailable with the host. A shared cache simplifies sharing at the cost of a network call and another service to operate.”

What the answer must demonstrate: Explain per-instance copies.

Applied · Question 3

Does a 30-second TTL guarantee every read is less than 30 seconds stale?

Reveal a model answer

“Only under specified fill, age, and refresh rules. If a delayed reader fills an already old value with a new 30-second timer, its data age may exceed that bound. I would carry version or source timestamps when the age limit matters and define which moment starts the TTL.”

What the answer must demonstrate: Distinguish cache residency age and data age.

Applied · Question 4

Why can delete-after-write still return the old price?

Reveal a model answer

“Reader R can fetch version 8 before writer W commits version 9, then refill after W deletes the cache. The delete happened, but the late reader resurrected the old copy. I show that timeline and choose either bounded stale display or a stronger version-aware update protocol.”

What the answer must demonstrate: Locate the late refill, then the atomic check.

Applied · Question 5

Why not acknowledge orders from a write-back cache?

Reveal a model answer

“If the cache acknowledges before durable persistence and then loses the entry, the customer can lose an order already reported as saved. I would need a replicated durable log and a tested recovery protocol, or acknowledge only after the required durable commit.”

What the answer must demonstrate: Tie acknowledgment to a loss model.

Applied · Question 6

A two-entry cache receives insert A, insert B, read A, insert C. What do FIFO and LRU evict?

Reveal a model answer

“With two entries and eviction from existing entries, FIFO evicts A because it was inserted first. LRU evicts B because A was accessed more recently. This demonstrates that insertion order and access order are different.”

What the answer must demonstrate: Replay the actual ordering.

Applied · Question 7

Ten thousand readers miss P7 at once. What do you do?

Reveal a model answer

“I allow one refresh for P7 and coalesce the other requests behind it, with bounded waiting. If the product permits it, I serve a stale copy during refresh. I also limit database fallback globally so many different missing keys cannot overwhelm it.”

What the answer must demonstrate: Distinguish same-key and many-key bursts.

Applied · Question 8

How would you add a CDN to an existing image service?

Reveal a model answer

“I keep static objects behind a stable static hostname and point delivery through the CDN. I set origin access, TLS, cache headers, and versioned object paths. On a miss the edge fetches the origin; on a permitted hit it returns its copy. Private objects need a separate authorization-compatible plan.”

What the answer must demonstrate: Explain both migration and key correctness.

Blank-page exercise · 20 minutes

Build the answer yourself

Design a product cache, then explain a late refill after a price change and a total cache outage.

  • Trace first miss and second hit.
  • State key dimensions and freshness contract.
  • Replay the four-step stale refill.
  • Choose which entry to evict and limit concurrent requests to the database when the cache is unavailable.

Check that each component and design decision follows from your requirements and workload.

Recall the key ideas

Answer from memory before opening each card. Explain why the choice works and what it costs. Revisit missed cards tomorrow.

Caching: cache hits, misses, write policies and invalidationCache questionsRecall first, then reveal

What identifies the entry, which store has the authoritative record, how old may the copy be, and how is it refreshed?

Key → source → allowed age → refresh.

Return to lesson
Caching: cache hits, misses, write policies and invalidationInvalidation versus evictionRecall first, then reveal

Invalidation addresses validity; eviction addresses capacity.

Freshness versus space.

Return to lesson
Caching: cache hits, misses, write policies and invalidationA reader fetches $20, then a writer saves $25 and clears the cache. What can go wrong?Recall first, then reveal

The delayed reader can refill the cache with $20 after the writer cleared it. The refresh protocol must account for that order of events.

Read old → write new → clear → refill old.

Return to lesson

Final revision

Summary and interview notes

A cache is a reusable copy whose value depends on a correct key, a declared freshness policy and safe behavior when the copy disappears. Choose placement and read/write policies separately, and protect the authoritative source from both stale refills and sudden miss traffic.

Remember these points

  • A hit is usable only if its data and access policy are acceptable.
  • A delayed refill can resurrect an old value after delete-after-write invalidation.
  • A minimum accepted version blocks older cache refills only after it is installed. Keep that protection valid while old refill requests can still arrive.
  • Expiration governs age, invalidation governs acceptability, and eviction frees capacity.
  • Coalescing combines concurrent refreshes of one key; randomized TTLs spread expiry across different keys.

Interview tips

  • Draw the reader/writer timeline before claiming that invalidation is safe.
  • State whether freshness is measured from source validation or from insertion into the cache.
  • Distinguish no-cache from no-store when describing HTTP behavior.

Important qualifications

  • Checkout or permission decisions may need current authoritative state even when a product page tolerates stale display data.
  • A Redis eviction configuration does not implement the application’s consistency protocol.
  • Test an empty or unavailable cache while limiting how many fallback requests the database or origin server accepts at once.

Technical references

Practice marks stay in this browser.