Reviewing the Cache Key That Forgot the Locale

The cached fragment was keyed on the path only, so the first visitor's language was served to every subsequent visitor, and the mismatch looked like a…

Share
Reviewing the Cache Key That Forgot the Locale. Abstract code review illustration in orange and dark grey on debugly.dev

Between the English and the French user's page sit 6 quiet assumptions, and the incident is the story of one of them failing.

Here is the shape of the cache key that omits a dimension of the response, and the one property that makes it look like a different bug: the cached content is correct for someone, so the output is valid, and the valid output is the reason the translation is suspected rather than the cache.

This was a Next.js fragment cache, and the mechanics are the same for any cache keyed on a subset of the response's inputs.

What the review saw

The review saw a cache, and the cache had a key, and the key was the path, and the path was the request's identity, and the identity was the reasonable key, and the reasonable was the pass, because the path is the usual key, and the usual is the pattern, and the pattern was correct for the single-language site, and the site was not single-language, and the not single was the locale, and the locale was the missing dimension, and the missing was the collision.

The review did not ask what varies in the response, and the varies is the question, and the question is the key's design, and the design is the dimensions, and the dimensions are the inputs, and the inputs are the path and the locale and the auth and the device, and the four are the key, and the key was the one, and the one was the bug, and the bug is the review's question that nobody asked.

The missing dimension is the review's blind spot, because the key is present and the key looks right, and the right is the shape, and the shape is the pass, and the pass is the collision, and the collision is the incident, and the incident is the reason the review should enumerate what the response depends on.

Why it looked like a translation bug

The served page was English, and the English was the wrong language, and the wrong language was the translation, and the translation was the strings, and the strings were the file, and the file was the French, and the French was correct, and the correct was the confusion, because the strings were right and the page was wrong, and the wrong with the right strings is the cache, and the cache is the served content, and the served was the other visitor's, and the other was the English, and the English was the collision.

The confusion cost the day, because the translation was investigated, and the investigated was the strings, and the strings were fine, and the fine was the dead end, and the dead end was the hour, and the hour was the incident's duration, and the duration is the cost of the misdiagnosis, and the misdiagnosis is the cache's invisibility, and the invisibility is the reason the cache header belongs in the bug report.

This is the same misdirection as the WooCommerce cart cache that leaked between sessions, and the common root is the property, not the platform.

The questions that catch it

What does the response depend on? The answer is the dimensions, and the dimensions are the key, and the key must include every one, and the every is the completeness, and the completeness is the fix, and the question is the review's first, because the omitted dimension is the collision.

Who else gets this cached value? The answer is the audience, and the audience should be the people for whom the value is correct, and the correct is the key's scope, and the scope is the fix, and the question is the review's second, because the too-wide audience is the leak.

What changes the response without changing the key? The answer is the collision, and the collision is the bug, and the bug is the missing dimension, and the dimension is the fix, and the question is the review's third, because the unchanged key with the changed response is the collision.

Is the key derived or enumerated? The derived key is computed from the request, and the computed is the automatic inclusion, and the inclusion is the fix, and the fix is the derivation, because the enumerated key is the list someone wrote, and the list is the omission, and the omission is the bug.

The fix

Added the locale to the cache key. The key became the path plus the locale, and the plus was the dimension, and the dimension was the separation, and the separation was the fix, and the fix was the one line, and the line was the prevention.

Derived the key from the request rather than enumerating it. The key is computed from the dimensions the response depends on, and the computed is the automatic, and the automatic is the completeness, and the completeness is the fix, and the fix is the function, and the function is the single place the dimensions are declared, and the declared is the review's target.

Added the vary header. The response carries Vary for the locale, and the header is the CDN's instruction, and the instruction is the separation, and the separation is the fix, and the fix is the header, and the header is the shared cache's key, and the key is the edge's, and the edge's is the same bug at a different layer.

Tested two locales against the same path. The test requests the path in two languages and asserts the responses differ, and the assert is the collision's catch, and the catch is the regression guard, and the guard is the prevention, and the prevention is the test.

What I now do

A cache key must include every input the response depends on, so a key of the path alone serves the first visitor's variant to everyone else. Enumerate the response's dependencies, derive the key from them, set the Vary header for shared caches, and test two variants against the same path.

The French users saw English and the translation files were correct, because the cache key was the path and the path was the same in both languages, so the first visitor's fragment was served to everyone. The lesson I keep is the property, not the incident.


Published on debugly.dev by Rohit Bhadani | Founder of Krova Cloud