Cache Lab: Thundering Herd
This lab shows what happens when many workers miss the same expired cache key at the same time, then fixes it with a short Redis fill lock.
SET NX PX. Others wait and retry GET.Redis Commands
GET key
Reads the cached value. Redis returns nil when the key does not exist or has expired.
SET key value EX ttl
Stores the backend result with a TTL. In this lab the Go client passes a duration, which Redis applies as expiry.
SET key value NX PX ttl
Creates a short fill lock only if it does not already exist. This is the single-flight gate.
DEL key
Deletes the fill lock after the winner stores the cache value. This lock protects recomputation, not business correctness.
Keys In Redis
| Key | Type | Purpose | Expiry |
|---|---|---|---|
product:42:price | String | Cached product price. | 3 seconds in the lab. |
product:42:price:filling | String | Temporary marker for the worker filling the cache. | 10 seconds in the lab. |
Run Targets
The runnable lab is labs/redis/cache, backed by the shared Redis Go driver under labs/redis/cmd/cache.
make break
Runs naive mode. Look for many GET MISS lines and many backend calls.
make test
Runs fix mode. Look for one backend call per round and many workers waiting for the fill.
make load
Runs a bigger fixed scenario with more workers and rounds.
How To Read The Output
The important line is backend calls. In broken mode it grows with worker count. In fixed mode it should stay close to the number of expiry rounds.
Production Notes
- This is cache stampede protection, not a general distributed lock for correctness.
- The fill lock TTL must be longer than the expected backend fill time.
- If the backend call fails, the winner should release the fill lock or let it expire quickly.
- Hot keys may need jittered TTLs so many keys do not expire at the same time.
Code Pointers
| Code | Why it matters |
|---|---|
cmd/cache/main.go | The naive cache miss path, single-flight fill marker, and backend-call counter. |
cache/Makefile | Targets for broken, fixed, and larger load scenarios. |
cache/compose.yaml | Redis container used by the lab. |
cache/README.md | Short explanation of the cache-stampede invariant and expected output. |