# Cache-aside with invalidation on Upstash Redis

## Read path

1. GET product:123.
2. Hit: return it.
3. Miss: load from the source of truth, SET it with a TTL, return
   it.

The TTL is your backstop. Even if invalidation has a bug, stale data
dies on schedule.

## Write path: the writer invalidates

Whoever changes the source of truth deletes the affected cache keys
in the same code path. Not a separate cron, not eventually: the
same function that writes the database issues the DEL.

For a product update:

DEL product:123
DEL category:shoes:list

## Key naming that makes invalidation possible

Name keys by what invalidates them. product:123 is invalidated by
product 123 changing. A key like homepage:v7:2026-09-26 cannot be
invalidated surgically: you would need to know every date variant.
Prefer entity keys over rendered-page keys.

## Stampede protection

On a miss storm (cache cold, traffic spike), many requests miss at
once and hammer the origin. Mitigations:

- SET NX a lock key with a short TTL; only the lock holder loads
  from origin, the rest wait briefly and retry the GET.
- Stagger TTLs with jitter so keys do not expire in the same
  second.

## Verify

Update an entity, then GET its cache key: it should miss once, then
repopulate with fresh data. Load-test a cold cache and confirm the
origin sees one load per key, not one per request.