GitHub - telemetry-sh/cache-stampede-lab: Simulate cache stampedes, synchronized TTL expiry, and origin overload. · GitHub
/" data-turbo-transient="true" />
Skip to content
Search or jump to...
Search code, repositories, users, issues, pull requests...
-->
Search
Clear
Search syntax tips
Provide feedback
--><br>We read every piece of feedback, and take your input very seriously.
Include my email address so I can be contacted
Cancel
Submit feedback
Saved searches
Use saved searches to filter your results more quickly
-->
Name
Query
To see all available qualifiers, see our documentation.
Cancel
Create saved search
Sign in
/;ref_cta:Sign up;ref_loc:header logged out"}"<br>Sign up
Appearance settings
Resetting focus
You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.
Dismiss alert
{{ message }}
Uh oh!
There was an error while loading. Please reload this page.
telemetry-sh
cache-stampede-lab
Public
Notifications<br>You must be signed in to change notification settings
Fork
Star
main
BranchesTags
Go to file
CodeOpen more actions menu
Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit
History<br>1 Commit<br>1 Commit
.github/workflows
.github/workflows
cmd/cache-stampede-lab
cmd/cache-stampede-lab
docs
docs
stampede
stampede
web
web
.env.example
.env.example
.gitignore
.gitignore
LICENSE
LICENSE
README.md
README.md
go.mod
go.mod
View all files
Repository files navigation
Cache Stampede Lab
TTL jitter spreads expirations. It does not stop callers from stampeding the same hot key.
Cache Stampede Lab is a deterministic Go simulator for synchronized cache expiry. It places misses behind a finite origin worker pool and compares naive TTLs, TTL jitter, request coalescing, and stale-while-revalidate against the same seeded traffic.
Why this is interesting
Cache advice is often presented as a checklist: add jitter, use singleflight, serve stale data. Those controls solve different parts of the problem.
The default scenario has 1,800 requests per second, 24 hot keys, a 1-second TTL, and 16 origin workers. Its comparison is deliberately uncomfortable:
strategy p99 timeouts origin queue<br>naive 108ms 29.23% 55.37x 12295.5ms<br>ttl_jitter 247ms 44.45% 106.74x 20305.1ms<br>singleflight 119ms 0.00% 1.00x 2.4ms<br>stale_while_revalidate 3ms 0.00% 1.00x 2.4ms
TTL jitter desynchronizes different keys, but every request arriving after an individual hot key expires can still start a fill. In this workload, jitter creates more independent expiry waves without coalescing their callers. It is useful in combination with request collapsing; it is not a substitute for it.
The naive strategy's successful p99 looks deceptively low because requests that survive the queue are fast. Its 29% timeout rate and 12-second mean origin queue reveal the actual failure.
Run the interactive lab
Go 1.16 or newer is required.
go run ./cmd/cache-stampede-lab
Open http://127.0.0.1:3000. To choose another port:
PORT=3002 go run ./cmd/cache-stampede-lab
The browser application is embedded in the binary and served with net/http. There is no frontend build, framework, CDN, or runtime dependency.
Use the CLI
go run ./cmd/cache-stampede-lab compare --preset "Synchronized expiry"<br>go run ./cmd/cache-stampede-lab compare --preset "One viral key" --json<br>go run ./cmd/cache-stampede-lab compare --preset "Slow origin"
Use the simulation package
package main
import (<br>"fmt"
"github.com/telemetry-sh/cache-stampede-lab/stampede"
func main() {<br>config := stampede.DefaultConfig()<br>config.Strategy = stampede.Singleflight
result, err := stampede.Simulate(config)<br>if err != nil {<br>panic(err)
fmt.Printf("origin=%.2fx timeouts=%.2f%%\n",<br>result.OriginAmplificationFactor,<br>result.TimeoutPercent,
Model boundaries
This is a compact discrete-event model, not a production capacity planner.
Client requests arrive at a uniform rate with a seeded hot-key distribution.
Cache entries begin with synchronized expiry unless the jitter strategy is selected.
Origin fetches queue behind a fixed-size worker pool.
Late duplicate fills continue running and can extend a key's TTL.
Singleflight callers wait for the same in-flight fill.
Stale-while-revalidate serves cached data while one background fill runs.
Client cancellation does not remove work already accepted by the origin.
Eviction pressure, cache memory limits, network loss, multi-region propagation, and write invalidation are omitted.
Every strategy receives the same key sequence and origin service-time samples.
Optional aggregate telemetry
The lab runs offline unless TELEMETRY_API_KEY is set:
export TELEMETRY_API_KEY=your_project_api_key<br>go run ./cmd/cache-stampede-lab
The server sends aggregate events directly to the Telemetry ingestion endpoint. Only the strategy,...