Lesson 0015 · System Design · Modul Caching · ±20 menit

Cache stampede, hot key, dan cache berlapis

Saat cache justru menjadi sumber lonjakan: key populer yang kedaluwarsa, satu key yang membebani satu node, dan cara menumpuk cache supaya origin tetap tenang

Sebelumnya, lesson 0014 membahas TTL, eviction, dan invalidation. TTL punya efek samping: saat key yang sangat populer kedaluwarsa, semua request untuk key itu tiba-tiba miss bersamaan. Lesson ini menutup modul Caching dengan tiga masalah yang muncul hanya di traffic tinggi, dan kapan Anda tidak perlu memikirkannya.

Target lesson ini: Anda bisa menjawab "Setiap kali TTL halaman utama habis, CPU database melonjak. Kenapa, dan bagaimana memperbaikinya?" tanpa catatan, dalam 60 detik: menamai cache stampede, request coalescing/lock, serve stale while revalidate, jitter atau early expiration, dan angka QPS yang membenarkan semua itu.

1. Cache stampede (thundering herd)

Cache stampede (juga disebut dog-piling atau thundering herd) terjadi ketika item cache yang populer kedaluwarsa, sehingga banyak request melihat miss dan meregenerasi item yang sama pada saat bersamaan. [1] Penyebabnya justru pola cache-aside biasa: cek cache, kalau miss hitung ulang dan simpan. Selama hitung ulang berlangsung, setiap request baru juga miss.

Request 1 Request 2 Request 3 … N Cache: key expired Database semua miss N × hitung ulang
Tanpa proteksi: N request yang miss bersamaan menjadi N query identik ke database.

Angkanya cepat membesar. Paper yang sama memberi contoh item yang hitung ulangnya butuh satu menit dan diakses 10.000 proses per menit. [1] Hitung sendiri: key dibaca 5.000 kali per detik, hitung ulang 2 detik → sekitar 10.000 request bisa miss sebelum nilai baru tersimpan.

Empat mitigasi

  1. Request coalescing. Gabungkan banyak miss untuk key yang sama menjadi satu request ke origin. RFC 9111 menyebutnya "collapse requests": menggabungkan beberapa request masuk menjadi satu request ke depan saat miss, sehingga beban origin turun. [2] Di nginx: proxy_cache_lock on mengizinkan hanya satu request pada satu waktu mengisi elemen cache baru; yang lain menunggu sampai respons ada di cache atau proxy_cache_lock_timeout. [3]
  2. Lock di cache terdistribusi. Di level aplikasi, request yang miss mencoba mengambil lock dan hanya pemegang lock yang menghitung ulang. Redis SET lock:key 1 NX EX 10: NX hanya menulis kalau key belum ada [4], EX memberi lock TTL. Paper Vattani mencatat kelemahannya: request lain tidak punya nilai untuk dikembalikan (harus menunggu atau memakai nilai basi), ada write tambahan untuk lock, dan kalau pemegang lock gagal, tidak ada yang mengisi ulang sampai lock kedaluwarsa. [1]
  3. Serve stale while revalidate. Sajikan nilai lama sambil satu proses me-refresh di belakang. HTTP: Cache-Control: max-age=600, stale-while-revalidate=30 berarti segar 600 detik, lalu boleh disajikan basi sampai 30 detik lagi sementara validasi asinkron dicoba. [5] nginx: proxy_cache_use_stale updating plus proxy_cache_background_update on. [3]
  4. Early expiration dan jitter. Setiap request boleh memutuskan secara acak untuk menghitung ulang sedikit sebelum TTL habis, dengan peluang yang naik saat TTL mendekati habis; paper Vattani membuktikan versi eksponensialnya (XFetch) optimal. [1] Versi paling sederhana: tambahkan acak kecil pada TTL (TTL 300 ± 30 detik) supaya ribuan key yang diisi bersamaan tidak kedaluwarsa pada detik yang sama.

Latihan 1 — Cache stampede

Jawab tanpa scroll ke atas. Soal yang sama akan muncul lagi, diacak, di review 0019.

Kuis di lesson ini adalah feedback cepat, bukan bukti penguasaan; yang dicatat hanya hasil review 0019.

2. Hot key

Hot key adalah satu key yang menerima traffic jauh di atas key lain (profil selebritas, produk flash sale). Di cache terdistribusi, setiap key dipetakan ke satu node (hashing, lesson 0010), jadi menambah node tidak memecah beban satu key: node pemiliknya tetap kewalahan sementara node lain santai.

3. Cache berlapis

Cache berlapis (multi-level cache) menumpuk beberapa cache, dari yang terdekat dan terkecil ke yang terjauh dan terbesar. Setiap lapisan menyaring miss sebelum sampai ke lapisan berikutnya.

L1: local cache (µs)→ L2: Redis bersama (ms)→ Database
CDN edge→ regional edge cache→ Origin Shield→ origin

CloudFront memakai regional edge cache sebagai lapisan tengah, dan Origin Shield menambah satu lapisan lagi: request untuk objek yang belum ada di cache-nya digabung dengan request lain untuk objek yang sama, sehingga bisa hanya satu request yang sampai ke origin. [9] Harganya: setiap lapisan menambah satu tempat data bisa basi. Local cache per instance cepat menjadi tidak konsisten antar-instance, sehingga perlu TTL lebih pendek. [10]

Latihan 2 — Hot key dan cache berlapis

Jawab tanpa scroll ke atas. Soal yang sama akan muncul lagi, diacak, di review 0019.

4. Kapan tidak perlu

Sinyal: proteksi stampede dibenarkan kalau QPS satu key × waktu hitung ulang menghasilkan banyak query serentak (5.000 QPS × 2 detik ≈ 10.000), atau metrik database menunjukkan lonjakan tepat saat TTL habis. Pada 10 QPS dengan hitung ulang 50 ms, miss bersamaan paling banyak satu atau dua query: biarkan saja. Pecah hot key hanya setelah --hotkeys atau metrik per node menunjukkan satu node jauh lebih sibuk dari yang lain.

Biaya dan risiko: lock menambah write dan titik gagal baru (pemegang lock crash) [1]; salinan hot key harus diperbarui semuanya; setiap lapisan cache tambahan menambah tempat data basi dan jalur invalidation yang harus dijaga. [10]

Alternatif lebih murah: jitter pada TTL (satu baris kode); stale-while-revalidate atau proxy_cache_lock di proxy yang sudah ada (konfigurasi, bukan komponen baru) [3]; dan untuk konten publik, biarkan CDN yang menyerap traffic sebelum memikirkan local cache sendiri.

Latihan lisan — Ucapkan dengan suara keras

Pasang timer dan jawab dengan suara keras, seolah sedang interview. Jangan buka jawaban contoh sebelum selesai.

"Halaman utama Anda di-cache dengan TTL 60 detik. Setiap kali TTL habis, CPU database melonjak ke 100% selama beberapa detik. Apa yang terjadi, dan bagaimana Anda memperbaikinya?" (60 detik)
Bandingkan dengan jawaban contoh dan checklist-nya

"Ini cache stampede atau thundering herd: key populer kedaluwarsa, ribuan request miss bersamaan dan semuanya menghitung ulang ke database. Angkanya: kalau key dibaca 5.000 kali per detik dan hitung ulang 2 detik, ribuan query datang serentak. Perbaikan pertama adalah request coalescing: hanya satu request yang mengisi ulang, misalnya dengan lock SET NX EX di Redis atau proxy_cache_lock di nginx, sementara yang lain menunggu atau memakai nilai lama. Lebih baik lagi serve stale while revalidate, jadi user tetap dapat nilai lama sambil satu proses me-refresh di belakang. Saya tambahkan jitter pada TTL atau early expiration probabilistik supaya key tidak kedaluwarsa bersamaan. Lock wajib punya TTL, karena kalau pemegangnya crash, tidak ada yang mengisi ulang. Di QPS rendah semua ini tidak perlu."

Checklist: apakah Anda menyebut…

  • nama masalahnya: cache stampede/thundering herd, banyak miss bersamaan untuk key yang sama?
  • request coalescing atau lock: hanya satu request yang mengisi ulang?
  • serve stale while revalidate atau refresh di background supaya user tidak menunggu?
  • jitter pada TTL atau early expiration probabilistik supaya tidak kedaluwarsa serentak?
  • risiko lock: pemegang lock gagal, jadi lock butuh TTL (mis. SET NX EX)?
  • angka yang membenarkan: QPS key × waktu hitung ulang; di QPS rendah proteksi tidak perlu?

Lulus jika minimal 4 dari 6.

Bacaan lanjutan (opsional, tidak dihitung dalam durasi) Vattani, Chierichetti, Lowenstein, "Optimal Probabilistic Cache Stampede Prevention", PVLDB 8(8), 2015, ±15 menit untuk bagian 1–2 (definisi dan perbandingan solusi). Bagian bukti matematisnya boleh dilewati.

Berikutnya, lesson 0016: modul Asinkron dimulai dengan message queue vs pub/sub. Modul Data dan Caching diuji bersama di review 0019.

Istilah: GLOSSARY.md (bagian "Caching") · Indeks: semua lesson System Design

Ada yang belum jelas? Tanyakan ke saya (agent) di terminal, misalnya: "Apa beda request coalescing dan lock?", "Bagaimana XFetch memutuskan kapan refresh lebih awal?"

Sumber: [1] Vattani et al. 2015, "Optimal Probabilistic Cache Stampede Prevention", PVLDB 8(8) · [2] RFC 9111: HTTP Caching · [3] nginx: Module ngx_http_proxy_module · [4] Redis docs: SET · [5] RFC 5861: HTTP Cache-Control Extensions for Stale Content · [6] Redis docs: redis-cli · [7] Redis docs: Client-side caching reference · [8] Amazon DynamoDB: Using write sharding to distribute workloads evenly · [9] Amazon CloudFront: Use Origin Shield · [10] Azure Architecture Center: Cache-Aside pattern