skip to content

learn.smmeyer.devsystem-design04

system-design/04

foundations · core · ~3h practice

Caching & performance

outcome

Use caches deliberately, calculate their value, and contain stale-data and stampede failure modes.

Plan a practice session · ~3h including exercises
Read + trace the worked example
25 min
Assignment + constraint variations
100 min
Timed drill + repeated explanations
35 min
Closed-book recall + self-review
20 min

This is a suggested practice budget, including drawing, variations, and repeated drills. Reading is the first block. Split the work across sessions and use the rubric to judge when to move on.

why this matters

Caches make fast systems possible and correctness subtle. Senior answers discuss hit rate, ownership, invalidation, and what happens when the cache vanishes.

coverage

  • Cache layers: client, browser, CDN, service, query, and distributed caches.
  • Patterns: cache-aside, read-through, write-through, and write-behind.
  • Freshness: TTL, versioned keys, event invalidation, stale-while-revalidate.
  • Failure: stampedes, hot keys, penetration, eviction, and stale data.
  • Other levers: batching, pagination, compression, precomputation, materialized views, pools.

mental model

Caching trades repeated work for a freshness and invalidation contract.

  1. 1 →Request

    Choose key

  2. 2 →Cache

    Hit or miss

  3. 3 →Source

    Compute/read

  4. 4 →Fill

    TTL + version

Read in numbered order: request → cache → source → fill.

lessons

01A cache is a prediction

You predict that the same result will be needed again before it becomes invalid. Estimate working-set size and hit rate. A 95% hit rate can reduce source load 20×; a 50% hit rate often does not justify major complexity.

Calculate misses before celebrating hits. At 10k reads/s and 95% hits, the source receives 500 reads/s. At 90%, it receives 1,000. The cache looks healthy in both cases while database demand doubles. Measure the hit rate by traffic class and key popularity; an average can hide one expensive query that misses often. Include fill cost and bytes, not only lookup count.

Cache-aside means the application reads the cache, fetches the source on a miss, then fills the cache. This creates a race at expiry: many requests can all miss before any fill finishes. If a hot key gets 3,000 requests/s and recomputation takes 200 ms, about 600 callers arrive during that window. Request coalescing lets one recomputation serve them, subject to bounded waiting and failure handling.

Choose placement from reuse. A browser cache avoids a network trip; a CDN serves reusable public content near readers; an application cache can share expensive results across requests. The key must include every dimension that changes the result, including tenant, locale, or data version where appropriate. Omitting a dimension can return a fast but incorrect—or unauthorized—answer.

  • Key includes every input that changes output
  • Capacity targets the hot working set, not the full corpus
  • Negative caching prevents repeated misses

02Invalidation is product semantics

Ask how stale is acceptable. TTL gives bounded staleness and simple failure behavior. Event invalidation is fresher but can lose or reorder events. Versioned keys make old values unreachable and simplify rollouts.

A TTL bounds how long an entry may remain without refresh; it is not a guarantee that the cached value matches the source throughout that time. If an account balance changes immediately after fill, a one-minute TTL can serve the previous balance for almost a minute. Decide whether that is acceptable for the specific action. A display may tolerate a stale estimate while a transfer must check authoritative funds.

Invalidation has ordering races. A reader fetches version 4, a writer commits version 5 and deletes the cache, then the reader fills version 4. Deleting on write alone did not prevent a stale refill. Options include versioned keys, conditional fills, or accepting bounded staleness with a clear refresh policy. The right mechanism follows the product's freshness contract.

Jitter spreads expiry across many keys; it does not stop concurrent misses for one viral key. Combine protections deliberately: coalescing for the same key, early refresh or bounded stale responses where allowed, and source admission limits when fills fail. On permission revocation, decide whether cached data can remain and where authorization is rechecked. “The cache expires eventually” may be too weak for access control.

  • Use jittered TTLs to avoid synchronized expiry
  • Single-flight/coalescing suppresses stampedes
  • Serve stale during source trouble only when semantics allow

03Performance is broader than caching

Batch network calls, paginate unbounded results, compress large payloads, precompute expensive aggregates, and pool connections. Optimize the bottleneck you can name and measure.

Break latency into queueing, computation, storage, and network time. A cache only helps the reusable portion. If most latency is a full table scan caused by a missing index, repairing the query can be simpler and more predictable than introducing cache invalidation. If payload transfer dominates, compression or pagination may help more. Measure before choosing a mechanism.

Tail latency is especially sensitive to saturation. As arrival rate approaches service capacity, small bursts have less spare capacity to drain, so queues grow and slow requests become much slower. Average latency can remain acceptable while p99 fails. Load-test the intended traffic distribution, including cold starts, expensive keys, and long payloads; a warm uniform-key benchmark is an optimistic special case.

Test the cache as a dependency that can disappear. If losing it sends all traffic to a source sized for 5% misses, the source may face twenty times its expected load. Protect it with bounded concurrency, admission, and a planned degraded response. Capacity and correctness must remain understandable during a cold fleet, failed refresh, and full cache outage.

  • Batching trades latency for throughput
  • Precompute trades write/storage cost for read latency
  • Connection pools need queue and timeout bounds

worked example

A high hit rate can still overload the source

A service handles 10k reads/s. Cache hit rate falls from 95% to 90%; one hot key receives 30% of traffic and takes 200 ms to recompute.

  1. At 95% hits, the source receives 500 reads/s. At 90%, it receives 1,000/s: a five-point loss doubles source load.
  2. For the expiring hot key, 10,000 × 0.30 × 0.2 = 600 requests arrive during one recompute window.
  3. Coalesce concurrent recomputations for that key. Other callers wait or receive an explicitly bounded stale value.
  4. Add expiry jitter across keys and test cache loss. Protect the source with admission control even if the expected hit rate is excellent.

Design decision: Treat cache behavior as part of the source capacity and correctness contract, including staleness and cold starts.

Limit: One coalesced request assumes a successful recomputation and a correctly scoped coordinator. Failures, lock expiry, and multiple cache nodes need their own behavior.

Teaching scenario · traffic, prices, and capacity values are illustrative.

lab

interactive lab

Cache Stampede — concurrent misses at one expiry

1 · predict first

The service handles 10k RPS, 30% goes to one hot key, and recomputing it takes 200ms. Without coalescing, how many requests hit the source during that expiry window? Predict a count and show your assumptions.

A guess makes the comparison useful. This lab draft lasts until you leave the page.

pedagogical model — values are illustrative, not measured.

assignment

Produce: A cache policy plus a before/after load table for steady state, expiry, and a cold fleet.

Paper, a text editor, or a spreadsheet is enough. Model access and paid services are optional.

  1. Use the lab to predict three source-load changes before moving the controls; record your assumptions.
  2. Choose keys, TTLs, invalidation ownership, and a maximum stale age for a product-detail endpoint.
  3. Trace expiry under concurrency, then rehearse a cold-cache incident and revise the admission policy.

Change a constraint

  • The cached result is an account balance with stricter freshness needs.
  • Recompute time grows from 200 ms to 2 s while traffic stays constant.

Self-review rubric

  • Source load is calculated from misses rather than total traffic.
  • The policy names an acceptable stale age.
  • Cache loss cannot send unbounded traffic to the database.

If a criterion is missing, revise that part of the artifact before rating your practice below.

Compare a sample artifact after your attempt

One defensible answer under these assumptions. Compare the reasoning and checks with your work; other designs can satisfy the same contract.

Product details allow at most 30 seconds of stale display data; the database is authoritative. Account-balance decisions do not share this freshness allowance.

10k reads/s: 95% hits → 500 source reads/s
             90% hits → 1,000 source reads/s
             cold    → 10,000 possible source reads/s
Key: tenant + product + representationVersion
TTL: <= 30 s for this display contract; jitter below that maximum
Hot key: 3,000/s × 0.2 s fill = 600 arrivals during recompute
Policy: one coalesced fill/key; bounded wait or permitted stale response
Cold/failed cache: source concurrency cap + explicit overload response

The design protects the source during expiry and cache loss. A writer increments or invalidates a representation version to address refill races. For balances, obtain authoritative state at the consequential action boundary. If recompute rises to two seconds, the hot-key window contains about 6,000 arrivals, so merely choosing a TTL is insufficient.

Check your artifact

  • The hit-rate change doubles source demand.
  • Jitter spreads different keys' expiries; coalescing addresses concurrent misses for one key.
  • A cold fleet cannot admit all misses to a database sized for a small fraction of traffic.

A tempting mistake: A 95% steady-state hit rate is not a cache-loss capacity plan. Define the accepted degradation before adding more cache nodes.

Put this chapter into practice: Contain a cache stampede · guided worksheet →

cumulative practice

Relay · build and evolve a private file service

Project brief

Keep one design document for Relay, a tenant-aware file service. Start with 100k daily users, ten downloads per user/day, 10k uploads/day, 1 MB typical bodies, 30-day retention, and a 5× download peak. Files are private to workspaces. Treat these as fictional planning assumptions. Later chapters add previews, recovery, regional constraints, compute, and an assistant. Record each change rather than replacing the earlier version; the final review combines the whole packet. These activities share the chapter's existing practice budget.

Keep one project document across this track. Reconstruct earlier reasoning before opening notes, then revise the same artifact. Use part of the assignment and review budget for this work. If you enter mid-course, sketch the earlier decisions first using the linked chapters.

Add to your project: Relay's body-cache policy and cold-cache load table.

Recall Relay's peak download rate. Calculate source demand at 95% and 80% hits. Decide which data can be cached, which access checks remain authoritative, and how a private link behaves after revocation.

Compare cumulative reasoning after your attempt

At 57.9 peak downloads/s, misses are about 2.9/s at 95% hits and 11.6/s at 80%: four times the source load. Body caching must not bypass trusted tenant, membership, and expiry checks. A signed body URL has an explicit delegated lifetime; immediate revocation needs a serving path capable of checking current access. Keep source admission bounded during cold starts.

Keep your first attempt and revision together. If your answer differs, identify the assumption or mechanism that explains the difference before rating your recall.

senior signal

When adding cache, give the failure plan: “If Redis is unavailable, cap source concurrency and degrade; never let every miss stampede the primary.”

failure patterns

  • Caching personalized data without tenant/user identity in the key
  • Letting all clients retry on cache failure
  • Using TTLs that expire together
  • Treating cache as durable source of truth

knowledge checks

Start with these quick checks, then explain the closed-book questions in your own words.

  1. 01Best first stampede defense?

    select an option to check your answer

  2. 02A 90% hit rate reduces backing reads by roughly…

    select an option to check your answer

closed-book recall

Close the worked example and explain these from memory. Answers are self-reviewed. Drafts stay on this page; save anything you want to keep in chapter notes.

Rate the assignment and recall together

Use the assignment rubric as evidence. Completion is your own judgment; this score schedules practice.

interview drill

Reduce p99 latency for a model-catalog API from 900ms to 120ms while updates may take 30 seconds to propagate.

  • p99 120ms
  • 30s staleness
  • 50:1 reads:writes
  • regional
compare a strong approach after your attempt

Profile first. Put a versioned object at the CDN or regional cache with a 30-second TTL and stale-while-revalidate. Publish invalidations on update as an acceleration, not the only correctness mechanism. Coalesce misses and cap fallthrough to the source.

Compare decisions and failure behavior, then repeat with a changed constraint. Keep your own wording.

Use the timed mock workspace →

references

Sources offer deeper context. Provider capabilities and API contracts can change; check current official docs before implementing.

notes

saved locally