Build a CDN (13 scenes)
Scene 05 · Revalidation — the cheap question with the expensive wait
When stale, the edge sends a conditional GET with the ETag; origin replies 304 Not Modified — body is empty, but the round trip isn't.
Previously
When the edge's copy goes stale, it does not re-download the whole body — it asks origin a cheap question first.
Scene 05
Revalidation — the cheap question with the expensive wait
Diagram
On the left, the edge POP holding one cached cell with an **ETag** badge — an opaque version tag the origin attaches to a response so the edge can ask 'is this still v7?'. In the middle, two arrows: the outgoing **conditional GET** carries `If-None-Match: v7`; the incoming reply is either a thin green `304 Not Modified` (no body, but full RTT) or a thick blue `200 OK` (full body). At the bottom, a waterfall strip — the green segment is **wait-for-server** (origin RTT, paid on every revalidation), and the blue segment is body bytes (paid only when the ETag doesn't match).
The cell starts FRESH. After a moment its TTL expires and the next request triggers a conditional GET — the edge asks origin 'is this still v7?'. Origin returns a thin 304 (no body), the edge serves the stored bytes locally, and the cell goes back to FRESH.
Implementation
Edge.handleRequest
decides: serve local, revalidate, or bypass
1def handleRequest(url):2 cell = cache.lookup(url)3 if cell.cacheControl == 'no-store':4 return origin.fetch(url) # always full 2005 if cell.cacheControl == 'no-cache':6 return revalidate(cell) # ask every time7 # max-age: serve locally while fresh8 if cell.fresh:9 return cell.body # ~2 ms, no RTT10 return revalidate(cell) # stale -> ask
Edge.revalidate
conditional GET: 304 keeps stored body, 200 replaces it
1def revalidate(cell):2 headers = {}3 if cell.etag: # If-None-Match: "v7"4 headers['If-None-Match'] = cell.etag5 resp = origin.fetch(cell.url, headers) # full RTT6 if resp.status == 304:7 cell.refreshTTL() # body unchanged8 return cell.body9 # 200 OK: origin sent a new body + new ETag10 cell.body = resp.body11 cell.etag = resp.headers['ETag']12 return cell.body
Origin.handleGET
compares If-None-Match; sends 304 (no body) or 200 (full body)
1def handleGET(url, headers):2 asset = store.load(url)3 clientEtag = headers.get('If-None-Match')4 if clientEtag and clientEtag == asset.etag:5 # short-circuit: empty body, ETag in headers6 return Response(7 status = 304,8 headers = { 'ETag': asset.etag },9 body = None,10 )11 return Response(200, asset.headers, asset.body)