Skip to the main content

cache invalidation · a personal blog by christoph lühr

What a cache key is really claiming

A cache key is a promise, and it is worth reading it as one. It says: any two requests that hash to this string will accept the same answer. Everything that goes wrong with caching goes wrong because that promise was made carelessly.

The key is the dependency list

Write out what a cached value was computed from — every input, every flag, every piece of ambient state the code read without mentioning it — and you have written the key. If the list is complete, the key is correct by construction. If something is missing, you have not built a cache, you have built a way of serving one user's answer to another.

That reframing is most of the work. It turns an invalidation problem, which is about time and is therefore hard, into a naming problem, which is about being honest and is merely tedious.

Invalidation is a question about everything the cached answer was derived from, and about which of those inputs can change without anyone announcing it.

What that looks like here

The generator that built this page keys a rendered page on its own bytes, the config, the theme, and the set of icon files the capsule ships. That last one is not obvious, and it was not there at first: adding a favicon changes the head of every page, so a key that ignored it would serve pages from before the favicon existed, forever, with no way to notice.

Nothing in that key is a timestamp. A modification time answers a question nobody asked — it says when a file was touched, not whether what it says is still true — and a fresh checkout rewrites every one of them without changing a byte of content.

The failure mode is the interesting part

An under-specified key does not crash. It serves a wrong answer at full speed, with no error and no log line, and it keeps doing it until somebody notices by eye. That is why the honest move is to make the key say too much rather than too little: an over-specified key costs you a cache miss, which shows up in a graph, and an under-specified one costs you correctness, which does not.

Back to the posts