From the Nitro caching part of your Conrad Caching note.
defineCachedEventHandler for a route, cachedFunction (alias defineCachedFunction) for any async function, and cache in route rules for pages.base:group:name:key.json, so with base: 'redis', the default group nitro/handlers, a handler named product and getKey returning the id, the entry is redis:nitro/handlers:product:123.json. Predictable keys are what make purging possible.maxAge (fresh for this many seconds, default 1), swr (serve stale and refresh in the background, default on), staleMaxAge (how long past maxAge stale is still allowed, default unlimited), getKey and varies (what counts as the same page), group and name (the key's namespace), base (which storage mount), shouldBypassCache and shouldInvalidateCache (per request).base points at a mount. A server plugin can mount Redis or Valkey at runtime from runtimeConfig, so one cache serves every pod.Cache-Control: public, max-age=<maxAge>, s-maxage=<maxAge> and, with swr, stale-while-revalidate, plus an ETag of the body. For HTML that is usually rewritten to private, no-cache so browsers revalidate every time and the CDN stays out of it.removeItem), by prefix (getKeys then remove, which catches every variant), or by tag, which Nitro does not have, so you keep your own index of keys per product. Hard purge deletes, soft purge marks stale so the next visitor still gets the old copy while a fresh one renders. Redis, the CDN and the browser are purged separately; the browser cannot be, it relies on the ETag.Cache-Control: no-store; the server cache still works.maxAge and staleMaxAge are Nitro's rules; the TTL of the Redis key is the storage's rule and can be much longer."handleCacheHeaders sets public, max-age=3600, s-max-age=3600": the numbers follow the handler's maxAge, so a handler with maxAge: 3600 sends them, while Nitro's default is 1 second. The directive is spelled s-maxage. With swr on, stale-while-revalidate is appended too.
The empty group line: group (default nitro/handlers) and name form the prefix of every key, so entries of one handler can be cleared together with useStorage('cache').clear('nitro:handlers:…').
stale-while-revalidate (SWR), not SWL.
defineCachedEventHandler with maxAge: 10, call it three times and watch the response headers and timing; then set swr: false and compare.base: 'redis' and confirm with redis-cli keys '*' that the entries moved there./products/** for 60 seconds per device type and leaves /account/** alone, from memory."By default in the memory of each instance, which means every pod has its own cache and loses it on restart. To share it you mount a storage such as Redis, in a server plugin so the URL can come from runtime config, and point the cache at it with base: 'redis'. Then every pod reads and writes the same entries, a miss on one pod is a hit on the next, and a deploy does not empty the cache."
"Two things. On the server, an expired entry is served immediately and re-rendered in the background, so nobody waits for a render after the first one. In the response, it adds stale-while-revalidate to the Cache-Control the handler sends, suggesting the same behaviour to shared caches. For HTML I keep the first and drop the second, because the browser should revalidate every time and the CDN should not cache pages with many variants."
Nothing yet. Notes filed here show up on this page and the section overview.
Cloudflare's cache
What Cloudflare caches by default and what it passes through as DYNAMIC, the CF-Cache-Status values, Cache Rules and edge TTLs, purging, and why HTML belongs at the origin when it has many variants.
Conrad DevOps
Preparation for the platform part of the Conrad interview, written from your Conrad DevOps note — Cloudflare at the edge, the GitLab to GKE pipeline, Docker and Kubernetes, monitoring and A/B testing, your Develit architecture to compare, and the questions to ask them.