Merancang sistem yang tetap cepat saat beban melonjak: caching berlapis dari CDN sampai query cache, pola beban flash sale beserta jebakannya seperti cache stampede dan thundering herd, strategi scale horizontal, serta capacity planning dengan headroom untuk spike 10x

Setelah di episode 10 kalian membangun security architecture — threat model STRIDE, defense in depth, dan zero trust foundation — pada episode ini kita kerjakan quality attribute nomor dua di peta studi kasus kita: performance & scalability, dengan target eksplisit dari episode 5: p99 read ≤ 300 ms dan ketahanan spike 10x saat flash sale.
Performance dan scalability sering dicampuradukkan. Performance tentang kecepatan satu request; scalability tentang kemampuan menahan beban bertambah tanpa redesign. Keduanya dilayani tactics berbeda tapi saling bertumpu — dan keduanya paling murah didesain sejak awal, bukan dioptimalkan setelah insiden.
Desain dimulai dari karakteristik traffic, bukan teknologi:
Baseline : ~200 rps read-heavy (rasio baca:tulis ≈ 100:1)
Flash sale : pukul 20.00 mingguan → lonjakan 10x dalam <60 detik
Hot spot : produk diskon sama diserbu bersamaanTiga sifat ini menentukan semuanya: read-heavy berarti caching adalah senjata utama; lonjakan tajam berarti autoscaling reaktif saja terlambat (instance baru butuh menit untuk siap); hot spot berarti partition key tunggal bisa menjadi bottleneck meski kapasitas total cukup.
Cache efektif dikelola berlapis — tiap lapis menyaring permintaan agar tak pernah sampai ke database:
Browser cache → asset statis, API response singkat
CDN → halaman produk publik, image
App-level (Redis) → hasil query panas, computed data
DB buffer pool → hot pages (otomatis)Prinsip per lapisan:
Cache-Control yang benar menghilangkan mayoritas trafik origin.StockChanged/ProductUpdated dari outbox episode 8 menghapus key relevan.Tip
Ukur hit rate tiap lapisan sebelum menambah lapisan baru. Cache yang tidak dipantau adalah liabilitas: hit rate rendah berarti latensi ekstra gratis, sedangkan hit rate tinggi pada data salah berarti bug konsistensi yang tersamar sebagai "fitur".
Saat key panas kedaluwarsa, ratusan request simultan melewati cache dan menghantam database bersamaan — persis saat flash sale dimulai.
Mitigasi:
Single-flight → hanya 1 request rebuild, sisanya tunggu hasilnya
Lock + refresh → kunci per-key, proses lain pakai nilai basi
Probabilistic early refresh → perpanjang TTL acak sebelum expiringMirip stampede tetapi lintas service: satu komponen gagal/lambat, seluruh caller retry bersamaan dan menjatuhkannya permanen. Mitigasinya retry dengan exponential backoff + jitter (dibahas penuh di episode 16) plus load shedding di edge.
Satu key Redis menerima puluhan ribu QPS (produk flash sale) — satu shard cache jenuh padahal cluster lain idle. Mitigasi: replicate key panas ke beberapa node atau cache di-process memory aplikasi untuk key super-panas.
Horizontal scaling menuntut instance yang saling menggantikan sempurna:
Aturan ujinya: kill -9 satu instance acak kapan pun harus nol dampak bagi pengguna.
Read mudah: replika database, cache, denormalisasi. Write lebih sulit:
Async write → antrikan non-kritis (view counter, log)
Batching → gabung tulisan kecil per interval
Sharding → pecah baris per partition key
CQRS → pisahkan model tulis & baca (episode 8)Untuk studi kasus kita: order write saat flash sale ≈ 2 ribu tps peak — PostgreSQL single primary dengan connection pooling sanggup; sharding belum dibayar. Keputusan ini konsisten dengan ADR-0002.
Architect harus bisa menghitung kasar sebelum benchmark. Contoh untuk target kita:
Target : 2.000 rps checkout, p99 ≤ 800 ms
Per-instance : 100 rps pada p99 aman (hasil load test)
Instance butuh: 2.000 / 100 = 20 + headroom 50% = 30 pod
DB connection : 30 pod × 20 conn = 600 → wajib pooling (PgBouncer)
Cache : produk panas ±500 item × 4 KB = trivial;
fokus pada QPS, bukan ukuranAngka pastinya datang dari load test, tetapi estimasi ini menentukan bentuk arsitektur lebih awal: pooling wajib, autoscaling target ~30, pre-scale terjadwal.
Quality scenario tanpa verifikasi hanyalah harapan. Praktik minimal:
k6 run --vus 200 --duration 5m checkout-load.js
# thresholds di script: p95<800ms, error_rate<0.1%Jadwalkan tiga jenis: smoke test rutin di staging tiap rilis besar, spike test mensimulasikan lonjakan mendadak 10x, dan soak test mencari kebocoran memori/koneksi di durasi panjang. Hasilnya dibandingkan langsung terhadap response measures episode 5 — gap-nya adalah backlog arsitektur kalian.
Important
Load test tanpa dataset realistis menipu. Cache hit rate 99% di staging karena data kecil akan jatuh di produksi; pastikan volume data dan distribusi akses mirip produksi, atau angka p99 kalian fiksi yang menenangkan.
Inti yang harus dibawa pulang:
Di episode 12 selanjutnya kita masuk bab microservices: kapan modular monolith kita benar-benar perlu pecah, cara memotong service boundary, komunikasi antar service, dan saga pattern untuk transaksi lintas mereka. Sampai jumpa!