Crux Read real headers, handlers, and a single-flight lock across the caching stack, predict the behaviour, and pick the highest-leverage fix.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 14 min
Caching bugs are read in headers, handlers, and logs, not in prose. Read each snippet, predict how the layers will behave under real traffic, then choose the fix a senior would make first.
Goal
Practise the loop you run on every caching incident: read the directive or the code on the hot path, predict where each layer caches or leaks, and reach for the change that fixes the composition — not a louder TTL.
This is a public price-list API that the team wants cached at the CDN edge, refreshed often, and still served if origin goes down.
Quiz
Completed
What does this header actually do versus what the team wants, and what is the highest-leverage fix?
Heads-up Shared caches prefer s-maxage when present; max-age without s-maxage applies to both. More importantly there is no SWR or stale-if-error, so the team's graceful-refresh and outage-survival requirements are unmet.
Heads-up no-cache forces revalidation on every request, removing the edge offload the team explicitly wants and adding a round-trip per hit. It contradicts the goal of caching at the edge.
Heads-up A shorter max-age refreshes more often but still gives no edge/browser split, no graceful expiry, and no outage fallback. It tunes one number and leaves two requirements unmet.
Snippet 2 — the conditional GET handler
app.get("/articles/:id", async (req, res) => { const article = await db.getArticle(req.params.id); const etag = `"${article.version}"`; res.setHeader("ETag", etag); res.setHeader("Cache-Control", "private, max-age=0, must-revalidate"); // always sends the full body res.json(article);});
Quiz
Completed
The handler sets an ETag but the conditional-request optimisation never fires. What is missing, and what is the win when fixed?
Heads-up A quoted version string is a valid strong ETag. The defect is server-side: the handler never inspects If-None-Match, so it cannot emit a 304 regardless of ETag format.
Heads-up must-revalidate tells the cache to revalidate once stale — it is what triggers the conditional request. The missing piece is the server-side If-None-Match check that turns that request into a 304.
Heads-up Setting an ETag header does not auto-generate 304s unless the framework's conditional-GET middleware runs before res.json. Here the body is sent unconditionally, so no 304 is ever produced.
Snippet 3 — the stampede lock
async function getHot(key) { let val = await cache.get(key); if (val !== null) return val; // cold: every concurrent caller reaches here and recomputes val = await expensiveQuery(key); // hits the DB await cache.set(key, val, { ttl: 60 }); return val;}
Quiz
Completed
A hot key expires and 5,000 requests arrive in the same second. What happens, and what is the single-flight fix?
Heads-up cache.set on caller one does not block callers two through 5,000 — they already passed the null check and are running their own expensiveQuery concurrently. Nothing serialises them without an explicit lock.
Heads-up The entry just expired — that is the trigger. The TTL governs how long the recomputed value lasts, not how many concurrent misses pile on at the moment of expiry.
Heads-up A longer TTL makes expiries rarer but the dogpile is just as severe whenever one does occur. Single-flight (or probabilistic early expiry, or SWR) is what bounds the concurrent recompute.
A response is cached at 12:00:00. Requests arrive at 12:00:30, 12:01:30, and 12:07:00. No write or purge occurs.
Quiz
Completed
What does the shared cache serve at each of the three times?
Heads-up s-maxage counts age from when the response was stored, not from the last access. Only the 12:00:30 request is within the 60s fresh window.
Heads-up The point of stale-while-revalidate is exactly that the stale entry is served immediately and the refresh happens in the background — the 12:01:30 reader does not block.
Heads-up Past the SWR window the cache does not evict-and-error; it performs a synchronous (blocking) revalidation against origin, and only falls back to stale if stale-if-error is set and origin fails.
Recap
Read top to bottom: a Cache-Control header has to split edge from browser (s-maxage vs max-age) and carry SWR plus stale-if-error to refresh gracefully and survive an outage; an ETag is inert until the server reads If-None-Match and answers 304; a cold hot-key needs single-flight so 5,000 misses become one DB query; and SWR’s two windows decide whether a reader gets a fresh hit, an instant-stale-with-background-refresh, or a blocking revalidation. Diagnose from the header and the log, fix the composition, then re-check under the same load.