API Design

Production at Scale · Simulator 02

Cache hit ratio at scale

A cache in front of your origin is the highest-leverage latency lever you have. Move the hit-ratio slider from 0 → 99% and watch effective latency collapse — even a slow cache beats going to origin most of the time. Then push QPS into the millions and see how the origin is shielded. This is the model behind the caching lesson, made live.

InteractiveDrag the slidersModels rel-07
💡 How to Read the Simulator

Top bars: "no cache" vs "with cache" latency, scaled to origin latency. Bottom bar: how much of total QPS hits the origin (red) vs is served from cache (green).

By the numbers: cache math

Every request is served either from cache (probability h) or falls through to origin (probability 1 − h). The effective latency is the weighted average:

eff_latency  = h × L_cache + (1 − h) × L_origin

origin_QPS   = QPS × (1 − h)          # only misses reach origin
origin_load  = 1 − h                   # fraction of total traffic
speedup      = L_origin / eff_latency  # how many times faster vs no cache

# Worked example: h = 0.9, L_cache = 1 ms, L_origin = 50 ms
eff_latency  = 0.9 × 1 + 0.1 × 50  =  0.9 + 5.0  =  5.9 ms
origin_load  = 1 − 0.9             =  10%          # origin sees only 1 in 10 requests
speedup      = 50 / 5.9            ≈  8.5×

The nonlinearity is the key insight: going from 90% → 99% hit rate halves origin load again (10% → 1%) and cuts effective latency another ~5×. The last 1% of misses still dominates tail latency when L_origin is large — keep origin fast even behind a good cache.

Hit Rate Sensitivity Table

Hit Rate (h) Effective Latency (ms) Origin Load Speedup Factor Cache Reads → Write Sequence
0% (Cache Cold) 50.0 ms 100% 1.0× GET cache (miss) → GET origin (50ms) → SET cache
50% 25.5 ms 50% 2.0× GET cache (50% hit/miss) → GET origin (if miss)
90% 5.9 ms 10% 8.5× GET cache (90% hit) → Return 1ms
99% 1.5 ms 1% 33.3× GET cache (99% hit) → Return 1ms
99.9% 1.05 ms 0.1% 47.6× GET cache (99.9% hit) → Return 1ms
✅ Try this

1. Set QPS to 10M, hit rate 90%, L_origin 200 ms → origin sees 1M req/s, eff latency ~21 ms. 2. Raise hit rate to 99% → origin drops to 100k req/s, eff latency ~3 ms — same origin capacity now handles 10× more total QPS. 3. Now drag L_cache up to 10 ms → latency creeps back up; a slow cache kills the benefit. Keep your cache fast and your hit rate high.

⚠️ Modeled, not measured

This is a first-principles model of cache latency, not a capture of any company's production traffic. It assumes steady-state hit ratio, no cold-start, no cache stampedes, and no replication lag. Real systems see lower effective hit rates during deploys, TTL expirations, and traffic spikes. Treat the numbers as illustrative.

Under the hood: how caching actually works

Caching operates at the HTTP protocol layer using headers and gateways (like Redis or CDNs) to intercept requests before they hit the origin server. When a client requests a resource:

  1. Reverse Proxy Check: The CDN or load balancer checks its internal storage (usually key-value hashes indexed by URL and query params).
  2. HTTP Header Handshake: If cached, the proxy evaluates standard headers such as Cache-Control and ETag:
    HTTP/1.1 200 OK
    Cache-Control: public, max-age=3600
    ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
  3. Conditional Validation: If the local copy is stale, the client sends a conditional request with If-None-Match. If unchanged, the server returns 304 Not Modified (saving body bandwidth).

How to debug & inspect it

To inspect and troubleshoot cache behaviors, use the browser's Network tab or command-line HTTP clients to observe the latency and cache headers.

$ curl -i -H "Accept-Encoding: gzip" https://api.example.com/v1/assets/logo.png HTTP/2 200 OK Cache-Control: public, max-age=86400 ETag: "v1-Logo" X-Cache: HIT Age: 3600 # The X-Cache header shows the request was served directly from the edge cache

Symptom → Cause → Fix:

Symptom Likely Cause Fix
Cache hit rate is 0% Cache-Control is set to no-store or contains unique session cookies Remove dynamic cookie values from responses or set Cache-Control: public, max-age=300
Private user details are cached publicly Cache-Control: public applied to authenticated endpoints Modify endpoint response to return Cache-Control: private, no-store
Massive backend latency spikes on cache TTL expiration Cache stampede where all concurrent clients attempt to regenerate the cache at once Use locking/single-flight patterns on the origin or serve stale cache during background refresh

Math companion — this page trains intuition with numbers. Full design contracts live in rel-07-caching.html.

When not to stop at the simulator

Why the numbers matter

If you cannot re-derive the formulas on this page from first principles (arrivals, service time, capacity, amplification), you do not own the control. Recompute by hand once, then change one input and predict the chart.

Failure translation

Map each sim control to an ops action: raise limit → client 429; add cache hit ratio → DB QPS falls; add jitter → retry peak flattens; add workers → lag falls until lock or DB saturates.

Sources & further reading