# 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.