Browse pages
Caching
A cache answers repeated expensive reads from stored results instead of recomputing them, trading lower latency and source load for stale reads and invalidation work. This page states when that trade is justified, what each cache's staleness contract covers, how keys separate results, and how the system behaves when the cache fails. A team applies it once traces or load tests name a repeated expensive read, so each cache carries a staleness bound the team chose instead of one it discovers during an incident.
Stack: any
Layer: infrastructure
Activation#
Activation scope: local.
Applicable specification kinds: use-case.
The consumer enables cache after traces or load tests identify a repeated expensive read whose use case states the staleness it accepts. Expected future traffic alone does not activate it.
Baseline Relationship#
This extension replaces no baseline provision. It selects no provider package: a project that adds one records the decision and pins its version in the manifest.
This extension switches on setting: the expiry of each cache is a setting record, and the accepted staleness is the served query's freshness.
Standards#
Record Measured Cache Need#
ruleruleguidancelitespec/ext/cache#record-measured-cache-needA cache proposal MUST record its observed query, representative load, latency, source cost, and target.
Applies toartifact: any
Why
A cache costs invalidation, capacity, and recovery work for as long as it lives. The proposal records the observed query, load, latency, cost, and target so a reviewer weighs that cost against measured gain. A cache added without measurements stays until someone pays to remove it.
Checka reviewer answers
Reviewed forWhich cache proposals lack observed query, load, latency, cost, or target? Look at the proposal behind each cache.
Marked guidance: a reviewer answers this question.
Record Accepted Cache Staleness#
ruleruleguidancelitespec/ext/cache#record-accepted-cache-stalenessA cached query MUST state its accepted staleness as a freshness object with mode of bounded and its duration in within.
Applies toartifact: record:use-case
Why
Accepted staleness is the contract each reader relies on: it states how stale a read may be before it counts as a defect. The served query states it as {"mode": "bounded", "within": "PT5M"} on its output, so the cache and the query share one bound that is reviewed with the use case.
Checka reviewer answers
Reviewed forWhich cached queries lack a bounded freshness object with its duration on their output? Look at the served query beside each cache.
Marked guidance: a reviewer answers this question.
Remove Unjustified Caches#
ruleruleguidancelitespec/ext/cache#remove-unjustified-cachesA cache owner MUST remove a cache when measurements do not justify its operating cost.
Applies toartifact: any
Why
Traffic shifts and sources get faster, so a cache that paid for itself last year can cost more than it saves today. The owner re-checks the measurements and removes the cache when they no longer justify its operating cost. A cache nobody owns stays forever, and its invalidation bugs stay with it.
Checka reviewer answers
Reviewed forWhich caches stay past measurements that no longer justify their cost? Look at the owner reviews beside the operating cost.
Marked guidance: a reviewer answers this question.
Compose Cache Keys from Result Inputs#
ruleruleguidancelitespec/ext/cache#compose-cache-keys-from-result-inputsA cache key MUST include its data-shape version, every result-affecting input, and applicable tenant or actor partition.
Applies toartifact: any
Why
A key missing the data-shape version serves old shapes to new readers after a deploy. A key missing a result-affecting input serves one caller's answer to a different request. Tenant or actor partition keeps one party's results away from another's.
Example
post-summary:v2:tenant-42:post-17 identifies a versioned, tenant-scoped result.
Checka reviewer answers
Reviewed forWhich keys miss the data-shape version, a result-affecting input, or the tenant or actor partition? Look at the key construction.
Marked guidance: a reviewer answers this question.
Exclude Unsafe Cache Key Material#
ruleruleguidancelitespec/ext/cache#exclude-unsafe-cache-key-materialA cache key MUST NOT contain a secret, unbounded raw input, or personal data that is not already a hash.
Applies toartifact: any
Why
Keys appear in diagnostics, metrics, and provider administration tools. That copies the value into a store no one classified. An email address in a key is a personal-data record in the cache provider's key space. It sits outside every retention and erasure path the project defined. Data minimization applies to an identifier as much as to a field.
Example
A per-account cache key carries the account's opaque identifier. A key that carried the address instead is replaced by a hash of it, and the entry is bound to the account that owns it.
Checka reviewer answers
Reviewed forWhich keys contain a secret, unbounded input, or unhashed personal data? Look at the key construction.
Marked guidance: a reviewer answers this question.
Define Cache Invalidation#
ruleruleguidancelitespec/ext/cache#define-cache-invalidationA cached value MUST define expiry as a setting record, invalidating write events, an invalidation owner, and behavior after invalidation failure.
Applies toartifact: any
Why
Every cached value goes stale, and the only choice is whether the project decides the behavior up front or discovers it during an incident. Expiry, invalidating write events, an owner, and failure behavior state how long staleness lasts and who fixes it when invalidation fails. A cache without that contract serves stale data on a schedule nobody chose.
Checka reviewer answers
Reviewed forWhich cached values lack expiry, invalidating events, an owner, or failure behavior? Look at the invalidation contract of each cache.
Marked guidance: a reviewer answers this question.
Record a Manual Cache Invalidation#
ruleruleguidancelitespec/ext/cache#record-a-manual-cache-invalidationA manual invalidation MUST produce a record naming actor, invalidation scope, and reason, as an audit record in its category when the audit extension is selected.
Applies toartifact: any
Why
A manual clear changes what every later reader sees and leaves no trace in the data itself. Investigating a stale-read report afterward means knowing whether someone already cleared the entry. Article 30 of the GDPR treats an administrative action over personal data as processing that has a record.
Example
A command that clears one tenant's cached price list records the tenant, the operator, and the incident it was run for.
Checka reviewer answers
Reviewed forWhich manual clears lack a record naming actor, invalidation scope, and reason? Look at the invalidation runs.
Marked guidance: a reviewer answers this question.
Prefer Bounded Staleness#
ruleadviceguidancelitespec/ext/cache#prefer-bounded-stalenessA cache owner SHOULD use a short expiry when precise invalidation costs more than recomputation.
DeviationA measured staleness and operating-cost decision permits a longer expiry.
Applies toartifact: any
Why
Precise invalidation costs design and code, while a short expiry costs one recomputation per window. When recomputation is cheaper, a short expiry bounds staleness without the machinery. A longer expiry then needs a measured decision that says the extra staleness is worth its saving.
Checka reviewer answers
Reviewed forWhich long expiries lack a measured staleness and operating-cost decision? Look at the expiry beside the invalidation cost.
Marked guidance: a reviewer answers this question.
Keep Source Data Authoritative#
ruleruleguidancelitespec/ext/cache#keep-source-data-authoritativeA cache implementation MUST keep required business data authoritative in its source of record.
Applies toartifact: any
Why
The cache holds a derived copy that the system can drop at any time. Required business data stays authoritative in its source of record, so losing the cache loses speed and nothing else. A cache that owns the only copy turns every cache outage into data loss.
Checka reviewer answers
Reviewed forWhich required business data lives authoritatively in the cache? Look at the source of record beside the cached copy.
Marked guidance: a reviewer answers this question.
Define Cache Outage Behavior#
ruleruleguidancelitespec/ext/cache#define-cache-outage-behaviorA cache outage MUST use the source when capacity permits or return a documented degraded response.
Applies toartifact: any
Why
Cache infrastructure fails like any other dependency. The outage behavior states whether callers fall back to the source or receive a documented degraded response, so an outage degrades the product on purpose instead of by accident.
Checka reviewer answers
Reviewed forWhich caches lack a documented source fallback or degraded response? Look at the outage behavior of each cache.
Marked guidance: a reviewer answers this question.
Emit a Signal for Each Degraded Cache Path#
ruleruleguidancelitespec/ext/cache#emit-a-signal-for-each-degraded-cache-pathA cache path MUST emit a metric distinguishing a cache miss, a cache error, and a degraded response.
Applies toartifact: any
Why
A fallback that works is invisible. An outage runs until someone notices the source load instead. The three cases need different responses: a miss is normal, an error is an infrastructure fault, and a degraded response is a product behavior change. One counter for all three answers none of them.
Example
The metric carries the cache name and the outcome, following the OpenTelemetry semantic conventions for the client it wraps.
Checka reviewer answers
Reviewed forWhich cache paths share one counter across miss, error, and degraded response? Look at the emitted metrics.
Marked guidance: a reviewer answers this question.
Bound Cache Refresh Work#
ruleruleguidancelitespec/ext/cache#bound-cache-refresh-workA cache implementation MUST bound simultaneous refresh work when concurrent misses could overload its source.
Applies toartifact: any
Why
When a popular entry expires, every concurrent miss can trigger its own refresh and flood the source the cache was protecting. Bounding simultaneous refresh work holds that flood to what the source tolerates. Request coalescing and jittered expiry are two ways to meet the bound.
Example
One request refreshes a key while concurrent callers await the same bounded work.
Checka reviewer answers
Reviewed forWhich caches refresh without bound on concurrent misses? Look at the refresh path under concurrent load.
Marked guidance: a reviewer answers this question.
Avoid Unbounded Key Locks#
ruleruleguidancelitespec/ext/cache#avoid-unbounded-key-locksA cache implementation MUST NOT create an unbounded per-key lock inventory.
Applies toartifact: any
Why
Per-key locks trade duplicate refresh for memory, and the trade needs a ceiling. Untrusted or high-cardinality keys create one lock entry each, which exhausts memory under attacker-chosen or runaway keys. A bounded lock scheme sheds that state instead of growing it.
Checka reviewer answers
Reviewed forWhich lock schemes grow one entry per key without bound? Look at the lock state under high-cardinality keys.
Marked guidance: a reviewer answers this question.
Conventions#
Place Cache Access at an Outer Boundary#
conventiondefaultguidancelitespec/ext/cache#place-cache-access-at-an-outer-boundaryPlace cache access in Infrastructure or a frontend data boundary.
ReplacementA consumer records a cache-placement convention naming where its cache access lives.
Applies toartifact: any
Why
Cache access lives in Infrastructure or a frontend data boundary, behind a port the use case owns when Application needs one. Domain code takes no cache dependency, so business rules read the same with or without a cache. A cache concern inside Domain would couple every rule to the cache's presence.
Checka reviewer answers
Reviewed forWhich cache access lives inside Domain, or outside the recorded local convention? Look at the cache call sites.
Marked guidance: a reviewer answers this question.
Dependencies#
Packages: None.
Related extensions: audit for manual invalidation records.
Guides: None.