CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/cache-coherence-patterns-reference

Pure-reference catalog of cache-coherence patterns across the request path. Defines the five-tier cache stack (browser → CDN → reverse-proxy → application → data store), the per-tier cache-writing patterns (cache-aside, write-through, write-back, write-around, refresh-ahead), and the canonical invalidation strategies (TTL-only, event-driven purge, surrogate keys, version-tagged URLs, soft purge), plus an anti-pattern table and a worked multi-tenant coherence-test example. Deep detail - the RFC 9111 Cache-Control / Vary / ETag directive tables and the cross-tier coherence + per-tier test surface - lives in references/. Use for pattern selection, Cache-Control header design, and coherence audits; use a cache-key-collision check when the question is whether two requests in an existing system collide on a concrete key scheme. Consumed by redis-cache-tests, cdn-cache-purge-tests, varnish-test-vtc-syntax, browser-cache-control-tests, and the cache-key-collision check.

75

Quality

94%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files
name:
cache-coherence-patterns-reference
description:
Pure-reference catalog of cache-coherence patterns across the request path. Defines the five-tier cache stack (browser → CDN → reverse-proxy → application → data store), the per-tier cache-writing patterns (cache-aside, write-through, write-back, write-around, refresh-ahead), and the canonical invalidation strategies (TTL-only, event-driven purge, surrogate keys, version-tagged URLs, soft purge), plus an anti-pattern table and a worked multi-tenant coherence-test example. Deep detail - the RFC 9111 Cache-Control / Vary / ETag directive tables and the cross-tier coherence + per-tier test surface - lives in references/. Use for pattern selection, Cache-Control header design, and coherence audits; use a cache-key-collision check when the question is whether two requests in an existing system collide on a concrete key scheme. Consumed by redis-cache-tests, cdn-cache-purge-tests, varnish-test-vtc-syntax, browser-cache-control-tests, and the cache-key-collision check.

cache-coherence-patterns-reference

Overview

Keeping cached values consistent with their source of truth across tiers (browser, CDN, reverse-proxy, application, data store). Wrong coherence shows as stale data; wrong invalidation shows as cache stampedes per cache-stampede-reference. A pure reference consumed by per-tier test skills.

When to use

  • Designing the cache tiers for a new product / endpoint.
  • Auditing an existing cache for coherence bugs (stale reads after writes, cross-tenant cache leaks, layered TTLs that fight each other).
  • PR review of changes to cache headers, Vary, or invalidation triggers.
  • Investigating "users see stale data" reports.

How to use this reference

  1. Locate the tier(s) the value lives in from the five-tier stack - each tier (browser, CDN, reverse proxy, application, data store) has its own TTL and invalidation mechanism.
  2. Choose the write pattern for the application tier from the cache-writing patterns table (cache-aside, write-through, write-back, write-around, refresh-ahead) based on the read/write mix and how much consistency you need.
  3. Choose the invalidation strategy from the invalidation strategies table (TTL-only, event-driven purge, surrogate keys, version-tagged URLs, soft purge) based on the staleness window you can tolerate.
  4. Set the contract - design the Cache-Control directives, Vary key, and ETag validators per references/rfc-9111-http-caching-directives.md.
  5. Write the per-tier coherence test from the cross-tier problems and test-surface catalog in references/cross-tier-coherence-and-test-surface.md, then re-check the design against the Anti-patterns table below.

The five-tier stack

TierWhereCommon TTLInvalidation
BrowserCache-Control: privateminutes-hoursTTL only (or Service Worker code)
CDNCloudflare / Fastly / CloudFront / Akamaiseconds-daysPurge API or surrogate-key tag
Reverse proxyVarnish, nginxseconds-hoursVCL purge / nginx cache_purge
ApplicationRedis / Memcached / in-processseconds-minutesDirect delete / pub-sub broadcast
Data storePostgres query cache, RDS read replicassecondsReplication-driven

A coherence bug at any tier surfaces at the user. The test surface is layered; each tier needs its own coherence tests.

Cache-writing patterns

For application-tier caches (Redis):

PatternFlowWhen
Cache-aside (lazy load)Read miss → read source → populate → return; Write → invalidate cacheRead-heavy, eventual consistency OK
Write-throughWrite → write source → write cache (synchronous)Strong consistency, latency tolerable
Write-backWrite → write cache → async write to sourceBurst writes; data-loss risk on cache crash
Write-aroundWrite → write source (skip cache); reads do cache-asideWrite-heavy with rare re-reads
Refresh-aheadBackground refresh before TTL expiresPredictable read patterns; hot keys

Invalidation strategies

StrategyMechanismTrade-off
TTL-onlyJust let it expireSimple; possibly-stale window = TTL
Event-driven purgeSource-of-truth update fires a deleteCoupling; firehose at high write rate
Surrogate keys (Fastly, Varnish)Tag responses; purge by tagGroup-invalidation; coordination cost
Version-tagged URLs/api/users?_v=42; new version = new keyImmutable cache; full deploy per change
Soft purgeMark stale, keep serving until refreshUsed by stale-while-revalidate per stale-while-revalidate-reference

Worked example: a multi-tenant dashboard endpoint

Scenario: /api/users serves per-tenant dashboard data, is read-heavy, and must never leak one tenant's rows to another. Walk the four decisions from How to use this reference, then the test.

  1. Tiers. The response flows browser → CDN → application (Redis) → data store. Because the payload is per-user, it must not sit in a shared cache: the browser tier gets Cache-Control: private and the CDN is bypassed for this route (or keyed per tenant), not left on the shared edge.
  2. Write pattern. Reads dominate and eventual consistency after a profile edit is acceptable, so the application tier uses cache-aside: a read miss loads from the data store and populates Redis; a write invalidates the tenant's Redis key.
  3. Invalidation. A profile edit is an urgent update, so TTL-only is not enough - pair the TTL with event-driven purge so the write fires a delete of the tenant's cache key immediately.
  4. Contract. Set Vary: Authorization so each tenant gets a separate cache entry (the fix for the cross-tenant leak in the Anti-patterns table), and add a content-hash ETag so an unchanged reload returns 304 Not Modified instead of the full body.

Coherence test (the browser-tier "write → reload → see old" case):

  • Arrange: tenant A loads /api/users; the response is cached.
  • Act: tenant A edits a user - which must fire the Redis purge - then reloads the page.
  • Assert (staleness): the reload shows the edited value, not the pre-write state. A failure here means the write path skipped the invalidate - the "cache-aside without write-then-invalidate" anti-pattern.
  • Assert (isolation): a request from tenant B with a different Authorization header never returns tenant A's cached rows, proving the Vary: Authorization split holds.

Anti-patterns

Anti-patternWhy it failsFix
Cache-Control: public on per-user dataShared cache leaks dataUse private for user-specific
Missing Vary: AuthorizationCross-tenant leakAdd to Vary or set private
s-maxage longer than session lifetimeLogged-out users see another user's dataMatch TTL to security window
TTL but no purgeStale-window = TTL even for urgent updatesImplement purge API + use surrogate keys
ETag generated per-request from now()Defeats the validationStable ETag from content hash
no-cache instead of no-store for sensitive dataBrowser still stores; just revalidatesno-store, no-cache, must-revalidate, private
Browser TTL = CDN TTL = origin TTLMulti-tier amplifies staleness instead of layering itOrigin lowest, CDN longer, browser shortest
Cache-aside without write-then-invalidateReads see pre-write state for TTL windowAlways invalidate on write
Vary: *Disables shared cache entirelyUse specific headers
Single Cache-Control for HTML + JSON + assetsOne-size doesn't fit; HTML often short, assets longPer-route directives

Deep references

The contract layer and the audit-and-test surface live in two companion references so this file stays a decision surface:

Limitations

  • RFC 9111 governs HTTP caches only. Application-tier caches (Redis) use their own semantics; coherence is application- enforced.
  • Doesn't specify replication. Read replicas, multi-region CDN have their own coherence layer.
  • No global invalidation. Cross-tier purge requires coordination; no built-in protocol.
  • Cache-Control parsing has implementation drift. Some CDNs ignore directives they don't recognise; verify per vendor.

References

  • RFC 9111 HTTP Caching: www.rfc-editor.org/rfc/rfc9111.html; full directive tables in references/rfc-9111-http-caching-directives.md.
  • RFC 8246 immutable: www.rfc-editor.org/rfc/rfc8246.html.
  • Cross-tier coherence problems + per-tier test surface (with their cross-references): references/cross-tier-coherence-and-test-surface.md.
  • RFC 5861 stale-while-revalidate / stale-if-error (companion): stale-while-revalidate-reference.
  • Companion catalog: cache-stampede-reference.
  • Cross-tenant leaks via cache: cross-tenant-data-leak-tests.
  • Consumed by: redis-cache-tests, cdn-cache-purge-tests, varnish-test-vtc-syntax, browser-cache-control-tests.
Workspace
testland
Visibility
Public
Created
Last updated
Publish Source
GitHub
Badge
testland/cache-coherence-patterns-reference badge