Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Negative Cache Entries and Stale-Read Contracts

Last updated: 5 Oct 20268 min read
tutorial
IntermediateBy AITrove Editorial

A negative cache entry records an absent result so repeated misses do not hammer the source. Absence can change quickly: a case may be created, permission may be granted, or an external integration may recover. An absent value is also different from an error. Caching a timeout as if the case did not exist can mislead users and hide an outage. Stale serving has a separate contract: a copy older than its fresh lifetime may be returned for a limited period while refresh runs or the source is impaired. That may be fine for a district total but unsafe for access, approval, payment, or a newly published private record. Every cached negative or stale result needs a scope, age limit, and invalidation story.

Working case

Reviewer 29 queries permit case 47 before it is imported, and an absence result is cached for 63 seconds. Thirty seconds later a worker creates the case. The reviewer still sees a false not-found response unless the write invalidates the negative entry or the client has another route to fresh data. A second incident occurs when a database timeout is caught and cached as not-found for 63 seconds, concealing an outage. The service separates confirmed absence from source failure, uses a shorter negative lifetime for import-sensitive lookups, invalidates on creation, and bypasses stale cache on permission checks. A public district count may use an aged value with a visible timestamp.

Implementation boundary

javascript
function cachedReadDisposition(state, ageSeconds, maximumStaleSeconds) {
  if (state === "source-error") return "fail";
  if (state === "confirmed-absent") return "not-found";
  return ageSeconds <= maximumStaleSeconds ? "serve" : "refresh-or-fail";
}
console.log(cachedReadDisposition("present", 63, 47));
// Output: refresh-or-fail

Represent cache states explicitly: present, confirmed absent, stale present, and source error. Give negative entries their own short lifetime based on the rate of creation and the cost of repeated misses; invalidate them when a matching record is created if the write path can identify the key. Never use a single falsy value for absent, empty list, denied, and timeout. Bind keys to tenant and representation identity. For stale reads, store the source version or fetched time and enforce a maximum age on every return, including during prolonged source failure. Refresh with bounded concurrency so stale permission does not become an accidental cache stampede. A mutation or authorization decision must consult current source state or a proven stronger mechanism; showing a stale list must not grant an action.

Cost and boundaries

Negative caching saves source queries for repeated absent keys but may delay visibility of new records. A shorter lifetime reduces that delay while raising miss traffic. Stale serving can preserve an informational page during a source problem, but the product must expose age when the value could affect a user's decision. Extra state and per-route policy make cache code more complex than a plain get/set. Track negative-hit rate, newly created records hidden by negative entries, stale age p95 and maximum, error-versus-absence counts, refresh failures, and source read load. If a cache entry survives far longer than the product's acceptable age, a low average age does not excuse it.

Failure trace

Query an absent case and cache that result, then create the case before negative expiry; check whether invalidation makes it visible. Turn a source timeout into an error and prove no absent entry is written. Deny one reviewer access to an existing case and ensure the denial does not contaminate another reviewer's cache key. Keep the source down longer than the stale-age limit; the informational view must stop serving the old value or clearly fail according to its contract. Return a stale case list that still shows an Approve button, then confirm the server rechecks current permission and version before applying any action.

Verification

  • Confirmed absence and source failure have distinct cache states.
  • New record creation invalidates or bounds negative results.
  • Stale age is enforced per route, and actions recheck source authority.

Practice drill

Define a confirmed-absence TTL of 29 seconds for a permit import workflow and a maximum stale age of 47 seconds for a district count. Query case 62 as two reviewers in different organizations, create it during the negative window, and simulate a database timeout. Record the exact cache state and response for each caller. Hold the count source down for 63 seconds and verify the stale limit. Submit an approval from an old list and assert the write path uses current source state, not the display copy.

Decision note

Cache absence and age as explicit states; never turn failure or old data into a new authority.

Common Mistakes

  • Caching a timeout as a legitimate not-found result.
  • Sharing a negative permission result across users or tenants.
  • Allowing a stale display value to authorize a mutation.

Related lessons

Application Cache Consistency and Capacity; Cache-Aside Fill Races and Version Guards; Cache Stampede, Singleflight, and Hot-Key Budgets; Cache Memory, Eviction, and Degraded Read Paths; Stale Revalidation and Versioned Purges; Tenant Scope in Cache and Background Work.

Apply and check

Build Project: permit cache recovery and consistency and review Web Development: application cache contracts quiz.

web-tech
web-development
Storage details