Hacker DirectoryHacker Directory

WEB / WEB-19.01

Application cache and invalidation

Follow repeated public-note reads with cache-aside; separate hits, misses, errors, and authoritative revision ownership.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Miss then hit

  1. Public read intent

    The application starts a public Tea note read. Application, cache copy and authoritative note store are local components in this process; neither the store read nor cache fill has happened yet.

    Application
    Public note read
    Own local cache
    No cache result
    Authoritative owner
    Authoritative Tea note; version1
    Result decision
    No result
  2. Cache miss

    No local entry exists; the miss does not prove absence in the authoritative store.

    Application
    Public note read
    Own local cache
    Miss; no cached value
    Authoritative owner
    Authoritative Tea note; version1
    Result decision
    No result
  3. Authoritative read

    The store returns version1/title and readCount becomes1; this counter is not a latency benchmark.

    Application
    Public note read
    Own local cache
    Miss; no cached value
    Authoritative owner
    Tea note/version1; readCount1
    Result decision
    No result
  4. Populate copy

    A separate public-note copy enters the cache. Later mutation of the object returned to the application leaves the stored cache copy unchanged; applicationMutationChangesCache=false observes that boundary.

    Application
    Public note read
    Own local cache
    Cached Tea note/version1
    Authoritative owner
    Tea note/version1; readCount1
    Result decision
    No result
  5. First result

    The application uses the public note from the first miss; HTTP arrival is not modeled here.

    Application
    Public note read
    Own local cache
    Cached Tea note/version1
    Authoritative owner
    Tea note/version1; readCount1
    Result decision
    First result Tea note/version1
  6. Later cache hit

    A later selected read finds the cached copy; store readCount stays1 without a general newest-state guarantee.

    Application
    Public note read
    Own local cache
    Hit Tea note/version1
    Authoritative owner
    Tea note/version1; readCount1
    Result decision
    First result Tea note/version1
  7. Reused result

    The same public-note task completes from a cache hit; no production performance measurement is made.

    Application
    Public note read
    Own local cache
    Hit Tea note/version1
    Authoritative owner
    Tea note/version1; readCount1
    Result decision
    Reused Tea note/version1

Late old fill

  1. Public read intent

    The application starts a public-note read; the local cache is a copy and the store is the authoritative owner.

    Application
    Public note read
    Own local cache
    No cache result
    Authoritative owner
    Authoritative Tea note; version1
    Result decision
    No result
  2. Old read prepared

    An old reader has prepared candidate version1; its cache fill is not yet a committed result.

    Application
    Pending fill: Tea note/v1
    Own local cache
    No cache result
    Authoritative owner
    Authoritative Tea note; version1
    Result decision
    No result
  3. Store commit

    The authoritative owner commits Coffee note/version2; the pending old candidate does not update itself.

    Application
    Pending fill: Tea note/v1
    Own local cache
    No cache result
    Authoritative owner
    Authoritative Coffee note; version2
    Result decision
    No result
  4. Invalidate copy

    The cache entry is invalidated with known owner revision2; other process copies are not assumed synchronized.

    Application
    Pending fill: Tea note/v1
    Own local cache
    Empty cache; known revision2
    Authoritative owner
    Authoritative Coffee note; version2
    Result decision
    No result
  5. Old candidate

    The pending version1 candidate reaches the local fill boundary; selected owner current revision is2.

    Application
    Candidate1 vs current2
    Own local cache
    Empty cache; known revision2
    Authoritative owner
    Authoritative Coffee note; version2
    Result decision
    No result
  6. Reject old fill

    One serialized condition rejects unequal revisions; the cache stays empty with no distributed guarantee.

    Application
    Candidate1 vs current2
    Own local cache
    Rejected1; empty cache
    Authoritative owner
    Authoritative Coffee note; version2
    Result decision
    No result
  7. Fresh read

    A fresh authoritative read gets version2; committed state replaces the rejected old-fill candidate.

    Application
    Candidate1 vs current2
    Own local cache
    Rejected1; empty cache
    Authoritative owner
    Fresh Coffee note; version2
    Result decision
    No result
  8. Fresh result

    The application uses a version2 result; the sequence is verified only in selected local adapters.

    Application
    Candidate1 vs current2
    Own local cache
    Rejected1; empty cache
    Authoritative owner
    Fresh Coffee note; version2
    Result decision
    Coffee note/version2

Cache error

  1. Public read intent

    The application starts a public-note read; the local cache is a copy and the store is the authoritative owner.

    Application
    Public note read
    Own local cache
    No cache result
    Authoritative owner
    Authoritative Tea note; version1
    Result decision
    No result
  2. Cache error

    The injected unavailable cache raises an error; it is neither a miss nor authoritative-record absence.

    Application
    Public note read
    Own local cache
    Error; no cache result
    Authoritative owner
    Authoritative Tea note; version1
    Result decision
    No result
  3. Explicit fallback

    The selected public-read policy falls back to its authoritative owner; it is not assumed for every operation.

    Application
    Public note read
    Own local cache
    Error; no cache result
    Authoritative owner
    Fallback Tea note/version1
    Result decision
    No result
  4. Populate fails

    The cache remains unavailable so population success is not claimed; the authoritative result copy is retained.

    Application
    Public note read
    Own local cache
    Unavailable; not populated
    Authoritative owner
    Fallback Tea note/version1
    Result decision
    No result
  5. Store-owned result

    The application uses its authoritative public result; an owner error would create no empty/default note.

    Application
    Public note read
    Own local cache
    Unavailable; not populated
    Authoritative owner
    Fallback Tea note/version1
    Result decision
    Tea note/version1; source=store

ACTORS · STATE · DATA FLOW

Which work does a cache avoid?

An application cache reuses computed results or data read from another owner to reduce repeated work. The first public Tea note read misses: the application reads version=1/title from the authoritative store, copies it into the cache, and uses it. A later hit returns the selected value without another store read. Our fixture observes this with a read counter, not latency measurements or a claim that every workload becomes faster. Query results, computed views, and raw objects are different caching units; we select one public note.

This is separate from browser HTTP caching. A process-local dictionary gives different application instances their own copies. An in-memory service such as Redis can instead be a shared cache owner over a network; memory storage or a Redis label does not guarantee persistence or consistency. Redis server-assisted client-side caching can manage application-memory copies through invalidation notifications. The native adapter here stays in one process and executes no Redis connection or tracking.

A hit finds a cached result for a selected key; it does not generally prove the newest authoritative state. A miss does not prove database absence and needs an authoritative read. An error means cache access failed. This public-read fixture explicitly falls back to the authoritative store on errors; if that owner is unavailable too, it creates no empty note. Error policy is application-specific and is not automatically appropriate for sensitive or state-changing operations.

Which revision does invalidation protect?

A cache-aside write commits the authoritative update before invalidating the corresponding entry. Invalidating before commit lets an intervening reader refill the previous committed value. Even the intended order does not guarantee consistency under every concurrent or topology condition. External writers, delayed reads, and other local caches can reintroduce old data. TTL can bound staleness, but does not guarantee the newest value before expiry.

In the stale-fill case a reader has prepared version=1 when the owner commits version=2 and invalidation occurs. The old fill candidate then returns. One serialized adapter checks its known current revision=2 and rejects candidate=1; the next read obtains version=2. This authored version guard applies only to that owner access/update contract, not distributed consistency in general. Redis documentation separately addresses invalidations racing with delayed read responses; our local guard does not execute that protocol.

2. Red · Trust & Impact

Read every scenario without motion

Old fill

  1. The old read has not finished

    The reader has prepared the Tea note result at version=1. This copy has not entered the cache and will not update itself when the owner changes.

    Pending reader
    candidateVersion=1
    Authoritative note owner
    Tea note / version=1
    Cache admission boundary
    Fill pending
  2. The owner committed a new revision

    The authoritative record is now Coffee note at version=2. The pending reader still carries its old copy; one owner’s commit does not automatically change another state.

    Pending reader
    candidateVersion=1
    Authoritative note owner
    Coffee note / version=2
    Cache admission boundary
    Fill pending
  3. The cache is empty; the old candidate remains

    Invalidation removed the cached entry. Here the admission boundary knows owner revision=2. An empty cache does not mean every pending read is fresh.

    Pending reader
    candidateVersion=1
    Authoritative note owner
    Coffee note / version=2
    Cache admission boundary
    knownCurrentVersion=2 / cache empty
  4. Compare both revisions in one decision

    The old candidate’s version=1 does not match currentVersion=2 at admission. The decision uses the revision actually carried by the candidate.

    Pending reader
    candidateVersion=1
    Authoritative note owner
    Coffee note / version=2
    Cache admission boundary
    1 != 2 / fill decision pending
  5. The old copy was rejected

    With accepted=false, the old fill does not alter the cache, which remains empty. This guard applies to this serialized owner boundary where the current revision is known.

    Pending reader
    candidateVersion=1 / rejected
    Authoritative note owner
    Coffee note / version=2
    Cache admission boundary
    accepted=false / cache empty
  6. A new read returns to the current owner

    The next read returns version=2. Rejecting the old fill and obtaining a new result are separate observations. This does not establish synchronized copies in other processes.

    Pending reader
    nextResultVersion=2
    Authoritative note owner
    Coffee note / version=2
    Cache admission boundary
    old fill accepted=false

Owner also down

  1. The public read awaits a result

    The application requests the public note. Its cache copy and authoritative record have separate owners; the result source is not yet known.

    Pending reader
    Public note result pending
    Authoritative note owner
    Not read yet
    Cache admission boundary
    Cache outcome pending
  2. Cache access failed

    Cache outcome=error is different from a miss. The policy for this public read will attempt an authoritative read.

    Pending reader
    Fallback decision pending
    Authoritative note owner
    Not read yet
    Cache admission boundary
    cacheOutcome=error
  3. The fallback owner is explicit

    The application’s fallback source is the authoritative store. It consults the record owner instead of inventing a note from the failed cache.

    Pending reader
    fallbackOwner=authoritative store
    Authoritative note owner
    Read result pending
    Cache admission boundary
    cacheOutcome=error
  4. The authoritative read also failed

    When the second owner is unavailable too, no validated note result is available. Treating the cache error as data absence does not resolve that gap.

    Pending reader
    No successful result
    Authoritative note owner
    Store unavailable
    Cache admission boundary
    cacheOutcome=error
  5. No default note is created

    bothUnavailable.result=error and createdDefaultNote=false. The application does not turn an incomplete read into a newly created empty record.

    Pending reader
    result=error / createdDefaultNote=false
    Authoritative note owner
    Store unavailable
    Cache admission boundary
    cacheOutcome=error
  6. Preserve the policy scope

    This decision belongs to the public read-only note example. Applying the same fallback to private data or all state-changing operations requires separate policy evidence.

    Pending reader
    result=error / no default
    Authoritative note owner
    No authoritative result obtained
    Cache admission boundary
    Public read policy

ASSUMPTION · BOUNDARY · CONTROL

A hit and freshness are different claims

Do not review a controlled cache using hit ratio alone. The artifact separates cache and authoritative versions: a stale copy can still be a hit. Returning an older public title is a mechanism condition, not private-data or policy-bypass reproduction. Identify the caching unit and allowed staleness. TTL must not substitute for newest state or a permission refresh.

Deleting one instance local copy does not update another instance. A shared cache does not settle every application copy or replica either. The risk condition is interpreting one owner completion as synchronization across all owners. The effect can be an incorrect revision or stale computed view. Only public notes are used; the native adapter measures no distributed invalidation or production workload.

Do not convert errors into database absence

An unavailable cache returns an error, not a miss. If the public-read fallback obtains an authoritative result, the application uses that owner and does not require successful cache population. If the authoritative owner is unavailable too, an error remains and no empty note is created. This selected policy does not prove all endpoints operate safely during outages; resource consumption and retry storms need different evidence.

Ordinary concurrency such as a pending old fill after invalidation can restore an older revision. The fixture observes unequal current-owner and candidate revisions and preserves state. The expected truth table is examined without running an attack by disabling a guard. Missing observations do not prove a clean cache: a zero selector count without a source event is insufficient evidence.

3. Blue · Secure development & defense

Read every scenario without motion

Fallback result

  1. Separate fields by owner

    The evidence producers are the local cache adapter and note-store reads. error, returnedVersion and readCount are fields of this example, not predefined Redis log fields. This review uses the error section. Cache outcome, fallback owner, returned version and population outcome answer different questions.

    Cache observation
    error / fields selected
    Result source
    Result source not read yet
    Evidence interpretation
    Interpretation pending
  2. Distinguish an error from a miss

    cacheOutcome=error shows unsuccessful cache access. Counting it as a miss would hide which component failed.

    Cache observation
    cacheOutcome=error
    Result source
    Result source not read yet
    Evidence interpretation
    Cache failure; not absence
  3. Read the fallback owner

    fallbackOwner=authoritative store. A successful result therefore comes from the record owner, not the cache.

    Cache observation
    cacheOutcome=error
    Result source
    fallbackOwner=authoritative store
    Evidence interpretation
    Result version pending
  4. The returned revision is separate evidence

    returnedVersion=1 identifies this read result. The number alone does not show repopulation or refreshed copies in other processes.

    Cache observation
    cacheOutcome=error
    Result source
    returnedVersion=1 / source=store
    Evidence interpretation
    Owner result exists; cache outcome separate
  5. Population did not succeed

    cachePopulationSucceeded=false remains. The application can return a note from its owner while the cache is still unavailable; these are different success criteria.

    Cache observation
    cachePopulationSucceeded=false
    Result source
    returnedVersion=1 / source=store
    Evidence interpretation
    Read success != cache recovery
  6. Choose the supported claim

    This record supports an authoritative-store result for this public read. It provides no evidence of cache recovery, latency gains or resilience under every outage condition.

    Cache observation
    cacheOutcome=error / population=false
    Result source
    returnedVersion=1 / source=store
    Evidence interpretation
    Public read fallback verified

Observation gap

  1. Select the stale-fill record

    The evidence producers are the local cache adapter and note-store reads. error, returnedVersion and readCount are fields of this example, not predefined Redis log fields. The teaching record compares candidateVersion with currentVersion. The admission claim also needs a fillAccepted observation.

    Cache observation
    candidateVersion=1 / currentVersion=2
    Result source
    fillAccepted not read yet
    Evidence interpretation
    Admission result unknown
  2. The admission field is missing here

    In this incomplete-observation branch, fillAccepted is not supplied. Filling it with false would substitute an assumption for evidence.

    Cache observation
    candidateVersion=1 / currentVersion=2
    Result source
    fillAccepted=unknown
    Evidence interpretation
    Admission result unknown
  3. The selector only counts what it sees

    A selector for accepted stale revisions may find no match. With the admission field absent, zero matches are not evidence of rejection.

    Cache observation
    candidateVersion=1 / currentVersion=2
    Result source
    fillAccepted=unknown / no matching record
    Evidence interpretation
    Zero matches != proven reject
  4. Identify the missing observation

    Request admission outcome and the current owner revision used for the same fill attempt. A different attempt’s result cannot complete this one.

    Cache observation
    candidateVersion=1 / currentVersion=2
    Result source
    Decision missing for this fill
    Evidence interpretation
    Same-attempt admission evidence required
  5. Compare with the complete example

    The complete lateFill record contains accepted=false and cacheEmptyAfterReject=true together. Its conclusion cannot be copied into an incomplete branch as if the same event had been observed.

    Cache observation
    candidateVersion=1 / currentVersion=2
    Result source
    complete example: accepted=false
    Evidence interpretation
    Missing branch remains unknown
  6. Retain uncertainty in the conclusion

    The incomplete branch ends with insufficient evidence. It proves neither correct nor incorrect cache behavior and keeps the missing field explicit.

    Cache observation
    candidateVersion=1 / currentVersion=2
    Result source
    fillAccepted=unknown
    Evidence interpretation
    Insufficient evidence

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

Make caching contracts observable

The evidence producers are the local cache adapter and note-store reads. error, returnedVersion and readCount are fields of this example, not predefined Redis log fields. Record which public representation and revision a cache key describes, and return a copy to the application. The first miss makes authoritative readCount=1; a later hit leaves it unchanged. These counts describe selected adapter operations, not latency or Redis statistics. Expiry, eviction, and missing retain the previous key-value lesson boundaries. The artifact makes clear that the cache does not own authoritative records.

The fallback is selected for a read-only public scenario, so cache and store errors yield separate observations. The ledger records the result owner when cache access fails and verifies that authoritative failure creates no default object. Actual applications also need purpose, permissions, and allowed staleness. HTTP Cache-Control does not automatically invalidate this server-side dictionary; the policies of these two cache layers differ.

Check commit, invalidation, and refill separately

The fixture commits version=2/title in the store, invalidation removes the entry, and the next read populates version=2. Each owner state is visible; successful invalidation is not an atomic cross-owner transaction. The same serialized guard rejects old pending candidate=1, leaving the cache empty until a fresh version=2 read. Observer reads verify this deterministic native sequence.

The guard requires a current revision within the same decision boundary. Different processes, delayed notifications and independent writers need their own scope design. An absent decision observation remains UNKNOWN; another request’s successful result cannot supply it.

NOW APPLY IT TO THE CASE

Which revision is carried by an old read result after cache invalidation?

Duru reads the public Tea note in Atlas. While the record owner changes its title to Coffee note, an earlier read still carries version=1. Invalidating the cache does not update that pending copy.

Which cache owner supplies the case representation? Evaluate key scope, freshness and viewer context together; a hit does not establish the correct viewer.

Open the related case file ↗

READ THE EXAMPLE TOGETHER

Public note cache/revision ledger

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "selectedPolicy": {
    "cache": "own process-local copy dictionary",
    "authoritativeStore": "own public-note memory adapter",
    "redisExecuted": false,
    "httpCache": false,
    "errorFallback": "authoritative read for public read only",
    "revisionGuard": "serialized known owner revision; not distributed guarantee"
  },
  "normal": {
    "firstResult": {
      "id": "tea",
      "title": "Tea note",
      "version": 1
    },
    "firstCacheOutcome": "miss",
    "storeReadsAfterFirst": 1,
    "secondCacheOutcome": "hit",
    "storeReadsAfterSecond": 1,
    "applicationMutationChangesCache": false
  },
  "update": {
    "committedValue": {
      "id": "tea",
      "title": "Coffee note",
      "version": 2
    },
    "order": [
      "store commit",
      "cache invalidate",
      "next read/refill"
    ],
    "cacheEmptyAfterInvalidate": true,
    "refillVersion": 2
  },
  "lateFill": {
    "candidateVersion": 1,
    "knownCurrentVersion": 2,
    "accepted": false,
    "cacheEmptyAfterReject": true,
    "nextResultVersion": 2
  },
  "error": {
    "cacheOutcome": "error",
    "fallbackOwner": "authoritative store",
    "returnedVersion": 1,
    "cachePopulationSucceeded": false
  },
  "bothUnavailable": {
    "result": "error",
    "createdDefaultNote": false
  },
  "selector": {
    "language": "XPath",
    "xml": "<audit><cache candidateVersion=\"1\" currentVersion=\"2\" fillAccepted=\"false\"/></audit>",
    "xpath": "count(/audit/cache[@candidateVersion != @currentVersion and @fillAccepted='true'])",
    "expected": 0,
    "positiveFixtureXml": "<audit><cache candidateVersion=\"1\" currentVersion=\"2\" fillAccepted=\"true\"/></audit>",
    "positiveExpected": 1,
    "scope": "Only authored protocol-decision observations, not product log field names or a live HTTP capture.",
    "missingFieldBehavior": "Missing observation is insufficient evidence; zero matches is not proof of correct runtime behavior."
  }
}
normal
Authoritative read counts for miss/hit.
update/lateFill
Commit/invalidate/refill and serialized revision guard.
error/bothUnavailable
Error fallback owner and absence of default state.
selector
Synthetic stale-fill acceptance decision.

Own local memory adapters, not Redis/ElastiCache, latency, or cluster measurements. The guard current-owner assumption is explicit.

Two reality checks

A cache hit proves the newest authoritative state.

A hit finds a cached result; freshness depends on a separate lifetime, invalidation, and consistency contract.

Invalidation prevents every old pending read from refilling the cache.

A selected concurrent sequence can return an old fill; a local condition needs an explicit revision-owner scope.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

Known current version is 2 but a late-fill candidate is 1. Is it accepted into the cache?

  1. No; cacheEmptyAfterReject=true and the next read returns version 2.
  2. Yes; cache filling always overwrites with the last completed request.
Read the explained answer

No; cacheEmptyAfterReject=true and the next read returns version 2. — The selected serialized owner prevents a stale candidate from obscuring new state.

Why does public-read fallback after a cache error differ from both-unavailable?

Selected lesson artifact fields; teaching example

error
{"cacheOutcome":"error","fallbackOwner":"authoritative store","returnedVersion":1,"cachePopulationSucceeded":false}
bothUnavailable
{"result":"error","createdDefaultNote":false}
  1. Every cache error is a cache miss that creates a default resource.
  2. An authoritative read is possible after a cache error; when both are unavailable, the result is error with no default note.
Read the explained answer

An authoritative read is possible after a cache error; when both are unavailable, the result is error with no default note. — ErrorFallback is a selected public-read policy; data is not invented for unknown authoritative state.

Why do store commit→cache invalidate→refill ordering and process-local scope not establish distributed invalidation guarantees?

Read the explained answer

The selected local dictionary is invalidated after the canonical-store write and refilled with version 2 on the next read. Its revision guard uses known serialized-owner state; it measures neither inter-node invalidation delivery nor replication. Application cache is not browser HTTP cache, and RedisExecuted=false.

□ I did not count a local guard as a distributed guarantee.

□ I separated application cache from HTTP cache.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

Sources and scope