Decision records
ADR-0012: Bounded in-memory response cache
- Status: Accepted (2026-10-05)
Context
Handoff §13: bounded memory cache first, optional disk cache second; correct keys, Vary
handling, stale-while-revalidate where safe, tag/key purge and profile-driven bypass.
Handoff §8: never infer personalised-content cacheability from AI.
Decision
- Opt-in per application (
cache.enabled). One server-wide memory budget (server.cache.memory), entries aboveserver.cache.max_entry_sizeare not stored. Storage ismoka(TinyLFU admission, size-weighted eviction), not a hand-written LRU. - Only explicit freshness:
s-maxage,max-ageorExpiresfrom the response, or an operator-configureddefault_ttlfor200responses without any freshness information. No heuristic freshness. - Bypassed entirely (no lookup, no store): methods other than GET/HEAD, requests with
Authorization,Range, orCache-Control: no-cache | no-store/Pragma: no-cache. - Never stored:
responses with
Set-Cookie,Cache-Control: private | no-store | no-cache,Vary: *, non-cacheable statuses, or bodies that did not complete. Requests matching the application’s bypass rules (profilecache_bypassplus configuration: path prefixes, cookie-name prefixes, query parameters) skip the cache entirely. - Key: normalised host + raw path and query; each key holds up to 8 variants
distinguished by the request values of the headers named in
Vary. - HEAD is answered from a GET entry. A successful unsafe request (POST, PUT, PATCH, DELETE) invalidates the key of its URL (RFC 9111 §4.4).
- stale-while-revalidate (response directive or
cache.stale_while_revalidate): within the window a stale entry is served while one background request refreshes it (single flight per key);must-revalidatedisables it. - Purge by URL, URL prefix,
Cache-Tagor everything throughPOST /cache/purgeon the loopback metrics/admin listener (until the admin API, M9). - Responses carry
X-Cache: HIT|MISS|STALE|BYPASSandAgeon hits. - Disk tier: deferred until the memory tier is proven (handoff §13).
Consequences
- Applications must emit correct
Cache-Controlto benefit; nothing personalised is cached by guesswork. - Request coalescing (collapsing concurrent misses) is not implemented yet.