Interview questions
What a cache is, and what it costs
1Why is a cache always potentially stale, even when it is working correctly?
A cache is a second copy of the data that can be read without asking the original, and nothing forces the two copies to agree. So staleness is the default, not a bug. It is bounded only by what you add on top: a time-to-live (TTL), invalidation messages, or version numbers.
Read moreThe first copy with no obligation
2A cache serves 95% of reads. How dependent is the system on it?
Very. The store behind it now sees only 1 read in 20. This dependency, the cache's leverage, is 1 / (1 − h), where h is the hit rate: at h = 95% it is 20 to 1. If the cache disappears, the store gets 20 times the load it normally sees, and it was probably sized for the normal load.
3The hit rate falls from 99% to 98%. What happens to the database behind the cache?
Its load doubles. Misses go from 1% of reads to 2%, so at λ reads per second the database sees 0.02λ instead of 0.01λ. A one-point drop looks small on a dashboard and is a 2× change for the store.
4When does caching a computed result pay for itself?
When each version of the result is read often enough to repay the cost of producing it. As an inequality, λ / w > P / S:
- λ is reads per second and w is changes (new versions) per second, so λ / w is reads per version;
- P is the cost of producing the entry, and S is what each hit saves.
If the data changes nearly as often as it is read, each entry is thrown away before it earns back P.
Read moreAmortization: does the cache pay?
5An object is read very heavily. Does that make it a good candidate for caching?
Not by itself. Volume does not decide; the ratio of reads to writes does, because it says how many reads each cached version serves before it changes. A heavily read object that changes on most reads gains nothing. A modestly read object that never changes is ideal.
Read moreAmortization: does the cache pay?
Hit rate, sizing and tiers
6What is reuse distance, and why does it predict cache hits better than the time since a key was last accessed?
Reuse distance is the number of distinct other keys accessed between two accesses to the same key. It is what matters because an entry is evicted when enough new, distinct items push it out, not when the clock moves. A key reused after an hour with nothing else touched in between still hits; a key reused after a second, with millions of distinct keys in between, misses.
7How can one trace of requests predict the hit rate of an LRU cache at every possible size?
By Mattson's result: under LRU, an access hits if and only if its reuse distance is smaller than the cache size in entries. So one histogram of reuse distances gives the miss ratio at every size at once, the miss-ratio curve (MRC). Sampling methods such as SHARDS make the histogram cheap to build from production traffic.
8For which eviction policies can you not read the miss-ratio curve off a reuse-distance histogram?
For any policy that is not a stack algorithm, one where a bigger cache always holds everything a smaller one would. LRU is one. FIFO, CLOCK, random eviction and any policy with an admission filter are not, and caches with variable item sizes break the property too. For those, each cache size has to be simulated.
9Key popularity is skewed. How does the amount of skew change the way a cache must grow as the data grows?
Popularity usually follows a Zipf distribution with exponent α: the larger α, the more traffic goes to the top keys. Above α = 1, the cache size needed for a given hit rate stays about the same however large the data set becomes. At or below α = 1, a fixed-size cache loses hit rate as the data grows, silently, with no change to your code or configuration.
Read moreThe popularity exponent and the phase transition at α = 1
10Your cache has a 95% hit rate. Why can it not improve your p99 latency?
Because the slowest 5% of requests are the misses. Every percentile above h, here above p95, is made only of misses, so p99 is a miss latency whatever the cache does. To improve the tail, make the miss path faster, hedge slow requests or shed load; a bigger cache does not help.
Read moreThe tail rule
11You can put a cache in front of the most requested service or in front of the slowest one. Which do you choose?
Usually the slowest. A cache is worth hit rate × (t_miss − t_hit): how often it hits, times the time each hit saves. Hits in front of a fast service save little, however many there are.
13Access to a million items follows a Zipf distribution. How much cache do you need for a 67% hit rate, and for 90%?
With N = 1,000,000, caching the top 1% gives about 67% of hits, and 90% needs about 25% of the data. The hit rate of the top k items is about ln k / ln N, so the benefit grows only with the logarithm of the cache size.
Read moreThe Zipf arithmetic
14A cache has a 90% hit rate, 0.1 ms per hit and 50 ms per miss. Where does the mean latency come from?
0.9 × 0.1 + 0.1 × 50 = 5.09 ms, and 98% of it comes from the misses. Hits and misses would contribute equally only at a 99.8% hit rate.
Read moreThe mean is not where you think
What to cache: results, metadata, views and absences
15You cache query results keyed by the query text. What else must go into the cache key?
Everything the result depends on that is not in the text: session settings, the user's identity and permissions, the snapshot (data version) the query read, and the values of volatile functions such as the current time. Leave any of these out and two users, or two moments, get each other's answers. Sometimes that is a security bug, not just a stale one.
Read moreWhat the key must contain
16Why is caching table metadata, such as schemas and file lists, such a good deal?
It has many readers, one dependency and rare changes: every query needs it, and it changes only when a write commits. In open table formats the manifests are immutable, so they can be cached forever; only the pointer to the current snapshot needs a short TTL.
Read moreMetadata caching
17A semantic cache answers a new query from the cached results of different queries. What is its main weakness?
Deciding whether it has a hit. It must prove that the new query's answer is contained in what is cached (query containment), which is NP-complete in easy cases and undecidable in general. So practical semantic caches are sound but incomplete, a partial hit can cost more than a plain miss, and the lookup gets slower as the cache grows.
Read moreSemantic caching
Freshness, invalidation and stampedes
19What are the ways to keep a cache close to the truth, and who pays for each?
There are three, and each is paid in a different currency:
- Coordination (write-through, invalidation broadcasts): the writer pays, with extra work on every write.
- Staleness (TTLs, serving stale data): the reader pays, silently, by sometimes getting old data.
- Verification (ETags, leases, conditional GETs): the reader pays a round trip, but without the payload.
There is no fourth currency; every freshness scheme mixes these three.
Read moreThe three currencies of freshness
20Write-through, write-around and write-back are often presented as three settings of one dial. Why is that misleading?
They mix two independent decisions. One is write routing: where a write goes (the cache and the store, the store only, or the cache first). The other is freshness bounding: how the cache is kept close to the truth (not at all, TTL, versions, invalidation or leases). You choose both, and some combinations of the two are unsafe and must stay empty.
21When should a write also go into the cache (write-through), and when should it skip the cache (write-around)?
Ask how likely the written key is to be read soon after the write. If the write predicts a read, such as a user's session, cart or own profile, write through so that the read hits. If it does not, as with event ingestion or bulk loads, write around, so that written keys do not push useful entries out of the cache.
Read moreWrite routing
22Why is write-back, acknowledging a write once it is in the cache, not a way to keep a cache fresh?
Because it changes what the cache is. The cache now holds the only copy of recent writes, so it becomes the source of truth: a crash can lose up to one flush interval of writes, evicting an entry depends on the store having accepted it, and flushing safely needs an ordered log. You have rebuilt durability inside the cache.
Read moreWrite routing
23On a write, why delete the cache entry instead of updating it with the new value?
Because nothing orders cache writes against store writes. If two updates race, the older value can reach the cache last and stay there indefinitely. A delete is idempotent: whatever the order, the next read misses and loads the current value.
Read moreWrite-invalidate versus write-update
24Your invalidation is correct and complete. Why do you still need a TTL?
Three things can still leave a wrong value in the cache:
- a reader misses, reads the old value from the store, and writes it into the cache just after the writer's delete (a populate-after-invalidate race);
- an invalidation message is lost;
- a writer you do not know about changes the store directly.
A TTL is the floor that eventually corrects all three.
Read moreThe cells that must stay empty
25What does a TTL cost, and what can you use instead?
A TTL forces one miss per hot key per period, even when nothing changed. One million hot keys with a 60-second TTL is about 16,700 forced misses per second, a cost that grows with the size of the cache, not with how often the data changes. Verification, such as an ETag with a conditional GET, instead asks whether the value changed, and pays for freshness with a round trip that carries no data.
Read moreFreshness bounding
26You put a version number in every cache key. How is that like MVCC in a database, and what does it cost?
The versions of a key form a version chain, entries for versions no one will read again are dead tuples, and eviction plays the part of vacuum. The cost is space: with V versions of each key alive, the useful capacity falls from C entries to about C / V. With Zipf popularity over N keys, that lowers the hit rate by about ln V / ln N, a cost spread over every reader.
Read moreVersioned keys are MVCC
27How can users always see their own writes when they read through a cache that is not kept coherent?
Return a version token from the write and include it in the user's later reads: a cached entry older than that version counts as a miss. For monotonic reads, where a user never sees time go backwards, also refuse any version older than the newest one the user has already seen. Sending the user to the same server every time (sticky routing) is a weaker substitute.
Read moreSession guarantees come for free
28A hot cache key expires. How many duplicate recomputations reach the origin?
By Little's Law, L = λ_k × W, where λ_k is the request rate for that key and W is how long one recomputation takes. At 1,000 requests per second and 2 seconds, that is 2,000 identical queries. Worse, the extra load slows the origin, so W grows and the stampede feeds itself.
Read moreStampede
29Name three ways to prevent a cache stampede, and what each one costs.
- Request coalescing: one request recomputes and the others wait for its result. It costs waiting.
- Probabilistic early expiry: each request may refresh the entry shortly before it expires, with a probability that rises as expiry nears. It costs a little extra work and needs no coordination.
- Serve-stale-while-revalidate: keep serving the old value while one request refreshes it. It costs freshness.
Also add random jitter to every TTL, so entries filled at the same moment do not all expire at the same moment.
Read moreCoalescingProbabilistic early expiryServe-stale-while-revalidateCorrelated expiry
30You are bringing an empty cache into service. Why ramp traffic up instead of waiting a fixed time before sending it everything?
What has to be protected is the origin: the misses must stay within what it can absorb. So admit traffic at a rate λ(t) that keeps (1 − h(t)) × λ(t) below the origin's capacity, where h(t) is the hit rate as the cache fills. A fixed timer ignores both the hit rate and the origin's spare capacity, so it is either needlessly slow or does not protect the origin at all.
Read moreWarming and prefetch
Eviction, admission and distribution
31Why can no eviction policy be optimal, and what follows from that?
Eviction is an online problem: the policy must choose without knowing future requests. For a cache of k entries, any deterministic policy can be made to miss k times as often as the best policy that knows the future, and LRU and FIFO both reach exactly that bound; randomized policies do no better than Θ(log k). So big improvements come from changing what reaches the cache rather than from cleverer eviction: hints, bypass, admission filters, and queries that declare they will not reuse what they read.
Read moreEviction has a proven limit
32Why is it cheaper to control what enters a cache (admission) than to choose what leaves it (eviction)?
Eviction needs exact metadata for every entry in the cache, and under LRU even a hit is a write to a shared list. Admission needs only an approximate popularity count, about ten bits per key in a count-min sketch, with a small "doorkeeper" filter and periodic halving to forget old popularity, and it can run off the read path.
33Why do distributed caches use consistent hashing, and what do virtual nodes add?
With hash(key) mod N, adding or removing one of N nodes moves (N − 1) / N of all keys, 90% at N = 10, so a membership change empties the cache and becomes an availability incident. Consistent hashing moves only about K / N of K keys. Virtual nodes, V positions on the ring for each server, even out the load, with the imbalance shrinking as 1 / √V; 256 is a common choice.
34One key is so popular it overloads its cache node. Why does better hashing not help, and what does?
Hashing spreads keys, not requests: one key always maps to one node. With Zipf popularity (α = 1) over a billion items, the top key alone carries about 4.7% of all traffic. Fix it by caching the hottest keys inside each client (a small near-cache), or by replicating the key on several nodes and picking one at random. Both cost the work of keeping those copies coherent.
Read moreThe thing hashing cannot fix