$npx -y skills add proyecto26/system-design-skills --skill cachingThis skill should be used when the user asks about a "caching strategy", "cache invalidation", "what to cache", "read-through vs write-through vs write-back", "cache eviction" (LRU/LFU/TTL), "Redis vs Memcached", "stale reads", or hits "thundering herd", "cache stampede", "cache
| 1 | # Caching |
| 2 | |
| 3 | Put a copy of hot data closer to the reader so most requests skip the slow path. |
| 4 | Caching is the highest-leverage move for read-heavy systems — and the easiest to |
| 5 | get subtly wrong, because a cache adds a second source of truth that can serve |
| 6 | stale or wrong data, and can *amplify* an outage when it misbehaves. |
| 7 | |
| 8 | ## When to reach for this |
| 9 | Reads dominate (a high read:write ratio from `back-of-the-envelope`); the same |
| 10 | data is read repeatedly; the datastore is the read bottleneck; or recomputation |
| 11 | is expensive. A cache buys read latency and offloads the origin. |
| 12 | |
| 13 | ## When NOT to |
| 14 | Write-heavy or read-once data (low hit rate — pure overhead). Data that must be |
| 15 | exactly current with zero staleness (a cache is a stale copy by nature; when |
| 16 | strict freshness is required, go to the source or use `consistency-coordination`). Don't |
| 17 | add a cache before a number shows reads are the problem (YAGNI) — it's a new |
| 18 | failure mode and a second thing to operate. |
| 19 | |
| 20 | ## Clarify first |
| 21 | - **Read:write ratio and hit rate** — is the working set cacheable? (→ `back-of-the-envelope`, 80/20.) |
| 22 | - **Staleness tolerance** — seconds? minutes? must reads see their own writes? |
| 23 | - **Working-set size** — does the hot set fit in RAM across cache nodes? |
| 24 | - **Consistency on write** — can the cache briefly disagree with the store? |
| 25 | - **Eviction trigger** — what's the access pattern (recency? frequency? time-bound?). |
| 26 | |
| 27 | ## The options |
| 28 | |
| 29 | **Where to cache** (often layered): client/browser → CDN edge (→ `content-delivery`) |
| 30 | → application/in-process → distributed cache (Redis/Memcached) → database buffer |
| 31 | pool. This skill focuses on the application and distributed layers. |
| 32 | |
| 33 | **Read strategy** |
| 34 | - **Cache-aside (lazy):** app checks cache, on miss reads the store and populates. |
| 35 | Use when reads are unpredictable; the default for most systems. |
| 36 | - **Read-through:** the cache library fetches from the store on miss. Use to keep |
| 37 | app code simple and caching policy centralized. |
| 38 | |
| 39 | **Write strategy** |
| 40 | - **Write-through:** write cache and store synchronously. Use when reads right |
| 41 | after writes must be fresh and slower writes are acceptable. |
| 42 | - **Write-back (write-behind):** write cache now, flush to store async. Use for |
| 43 | write-heavy/bursty paths that tolerate a small loss window. |
| 44 | - **Write-around:** write only the store; let the cache fill on read. Use when |
| 45 | written data is rarely re-read soon (avoids cache churn). |
| 46 | |
| 47 | **Eviction policy** |
| 48 | - **LRU** for recency-skewed access (most common); **LFU** for stable popularity; |
| 49 | **TTL** to bound staleness; **FIFO** rarely. Match the policy to the pattern. |
| 50 | |
| 51 | ## Trade-offs |
| 52 | |
| 53 | | Option | What it solves | What it worsens | Change it when | |
| 54 | |---|---|---|---| |
| 55 | | Cache-aside | Simple, resilient (cache down ⇒ just slower) | First read per key is a miss; risk of stale after writes | Misses are too costly → read-through + warming | |
| 56 | | Read-through | Centralized, clean app code | Couples app to cache lib; cold-start misses | Custom per-key load logic is needed | |
| 57 | | Write-through | Fresh reads after write | Slower writes; writes cached data that may never be read | Writes dominate and aren't re-read → write-around/back | |
| 58 | | Write-back | Fast, absorbs write bursts | Data loss window on crash; complex | Durability of recent writes is required | |
| 59 | | Write-around | No churn from write-only data | Recently written keys miss on first read | That data IS read right after write → write-through | |
| 60 | | TTL eviction | Bounds staleness automatically | Mass expiry can stampede the origin | Add jitter / soft-TTL refresh | |
| 61 | |
| 62 | ## Behavior under stress |
| 63 | A cache that misbehaves doesn't just stop helping — it can take down the origin. |
| 64 | |
| 65 | - **Thundering herd / stampede:** a hot key expires (or the cache restarts) and |
| 66 | thousands of concurrent misses hit the store at once. *Mitigate:* per-key locks |
| 67 | / request coalescing (single-flight), early/probabilistic refresh, TTL jitter. |
| 68 | - **Cache penetration:** requests for keys that don't exist bypass the cache every |
| 69 | time (often malicious). *Mitigate:* cache the negative result (short TTL), or a |
| 70 | Bloom filter in front. |
| 71 | - **Hot key:** one key (a celebrity, a viral item) exceeds a single node's |
| 72 | throughput. *Mitigate:* replicate the key across nodes, add a local/L1 tier, or |
| 73 | shard the value. |
| 74 | - **Eviction storm / cold cache:** after a flush or deploy, hit rate craters and |
| 75 | the origin sees full load. *Mitigate:* warm critical keys; ramp traffic. |
| 76 | - **Stale-after-write:** the store changed but the cache didn't. *Mitigate:* |
| 77 | invalidate on write, or write-through, or short TTL — pick per s |