Trade-off komponen
Ringkasan satu halaman untuk dibaca ulang menjelang interview, diurutkan sesuai tangga skala (Lesson 0025) dan dirangkum dari bagian "Kapan tidak perlu" di setiap lesson komponen. Angka sinyal adalah titik awal yang masuk akal untuk interview, bukan aturan baku.
Prinsip: ukur dulu, lalu perubahan termurah. Naik satu anak tangga hanya kalau angka menuntut. Patokan: 1.000 DAU ≈ di bawah 1 QPS rata-rata, cukup satu server + satu database (0004).
Tangga skala: delapan anak tangga
| Komponen | Kapan dipakai (sinyal) | Kapan tidak | Alternatif lebih murah / risiko |
|---|---|---|---|
| Ukur (golden signals, EXPLAIN) | Selalu pertama: latency, traffic, errors, saturation; query plan untuk query lambat [1] | Tidak pernah dilewati | Hampir nol biaya |
| Index, perbaiki query/kode (0009) | EXPLAIN menunjukkan scan seluruh tabel, query N+1, satu query mendominasi waktu | Kolom yang tidak pernah di-query; tabel yang sangat sering ditulis | Risiko kecil: index menambah overhead saat menulis |
| Cache (0013) | Baca:tulis tinggi (mis. 100:1), key sama dibaca berulang, p99/CPU database masih tinggi setelah index | 1.000 DAU; data yang tidak boleh basi (saldo, stok saat checkout); sebagian besar request miss | Header Cache-Control untuk browser/CDN, local cache kecil. Risiko: cache-aside "doesn't guarantee consistency between the data store and the cache" [2] |
| Scale vertical (0005) | CPU/memori di atas ±70% terus-menerus setelah query diperbaiki | Sudah di mesin terbesar yang wajar; masalahnya availability (tetap satu titik gagal) | Satu perubahan konfigurasi, tanpa ubah kode; ada batas atas |
| Read replica (0009) | Read-heavy (≥10:1), primary tetap jenuh oleh baca setelah index dan scale vertical; standby bisa melayani query read-only [3] | Bottleneck-nya write: replica tidak membantu sama sekali | Index, cache dulu. Risiko: replication lag (user tidak langsung melihat tulisannya; baca data sendiri dari leader) |
| Queue / asinkron (0016) | Pekerjaan lambat (detik) atau pihak ketiga tidak stabil, hasilnya tidak ditunggu user; lonjakan melebihi kapasitas downstream | User butuh jawabannya sekarang dan tujuan cepat; rata-rata beban (bukan hanya puncak) sudah melebihi consumer: queue tidak menambah kapasitas | Tabel job dengan SELECT … FOR UPDATE SKIP LOCKED untuk beberapa ratus job per menit [4]; queue terkelola sebelum cluster Kafka sendiri. Risiko: eventual, pesan ganda, butuh idempotency |
| Sharding (0010) | Write melewati kapasitas satu leader setelah tuning dan scale vertical; data tidak muat di satu server terbesar yang wajar | Beban baca saja (itu replica/cache); 1.000 DAU | Arsip data lama; partitioning di satu server, yang biasanya baru sepadan kalau "the size of the table should exceed the physical memory of the database server" [5]. Risiko: query lintas shard, resharding, hot partition; shard key hampir mustahil diganti |
| Microservices / pecah service (0024) | Beberapa tim saling menunggu deploy; satu modul butuh sumber daya sangat berbeda (mis. CPU 10×) atau siklus rilis sendiri | Jumlah user saja: 1.000–100.000 DAU masih muat di beberapa server di belakang load balancer | Pertahankan modular monolith (batas modul dijaga); monolith stateless di belakang LB; worker lewat queue. Risiko: panggilan jaringan, saga, tracing per service |
Komponen pendukung
| Komponen | Kapan dipakai (sinyal) | Kapan tidak | Alternatif lebih murah |
|---|---|---|---|
| Horizontal scaling + load balancer (0006) | App server jenuh sementara database longgar; target availability (99,9%) menuntut deploy tanpa downtime | Satu app server: LB hanya menambah satu hop; database yang jenuh | Scale up; DNS dengan beberapa record A, sederhana tapi kontrolnya sedikit [6]. Tetap tulis aplikasi stateless sejak awal |
| CDN, reverse proxy, API gateway (0007) | User jauh dari origin (RTT puluhan–ratusan ms); bandwidth konten statis besar; banyak service dengan auth/rate limit berulang | User di satu kota; satu monolith (gateway hanya menambah hop); API personal yang tidak bisa di-cache | Cache-Control yang benar, kompresi, nama file berversi |
| NoSQL tambahan (0008) | Akses hanya per key pada volume yang melampaui satu relasional yang sudah di-tuning; data graph/time series | Butuh JOIN dan transaksi; 1.000 DAU | PostgreSQL + kolom jsonb, yang mendukung index [7] |
| Datastore multi-node, quorum (0011) | Write harus tetap diterima saat node/region putus, pada skala di atas satu leader + standby | Satu region, 1.000 DAU: jawab "satu leader, konsisten" | Satu leader + standby + read replica; read-your-writes dari leader |
| Invalidation eksplisit, eviction khusus (0014) | Toleransi basi lebih pendek dari TTL yang masuk akal; hit ratio terukur rendah | Data yang boleh basi beberapa detik/menit | TTL pendek; allkeys-lru sebagai default eviction [8] |
| Proteksi stampede, pecah hot key (0015) | QPS satu key × waktu hitung ulang = banyak query serentak (5.000 QPS × 2 detik ≈ 10.000) | 10 QPS dengan hitung ulang 50 ms | Jitter pada TTL; stale-while-revalidate atau proxy_cache_lock di proxy yang sudah ada |
| Exactly-once, DLQ, ordering ketat (0017) | Duplikat berarti uang hilang dan efeknya sulit dibuat idempotent; urutan mengubah hasil | Metrik atau presence yang boleh hilang | At-least-once + consumer idempotent; upsert per primary key; satu transaksi database |
| Event-driven, outbox (0018) | Satu kejadian ditunggu beberapa layanan milik tim berbeda; ada database dan broker, kehilangan event = data tidak konsisten | Hanya satu penerima yang cepat dan stabil; semua efek di satu database | Panggilan langsung; satu transaksi; tabel job sebagai queue sekaligus outbox |
| WebSocket, streaming (0020) | Data harus sampai dalam hitungan detik dan polling mendominasi beban | 1.000 user polling tiap 30 detik = ±33 request/detik, ringan | REST + short polling; SSE untuk aliran satu arah |
| Rate limiter (0021) | API terbuka; satu client memakai porsi besar (mis. 40% traffic); endpoint mahal per request | Tool internal 50 karyawan | limit_req di nginx [9] atau throttling gateway; counter di memori kalau hanya satu server |
| Circuit breaker, bulkhead (0022) | Satu dependency lambat menyeret p99 semua endpoint; pool koneksi habis saat insiden | Monolith dengan satu database (kalau database mati, tidak ada yang diselamatkan) | Timeout ketat (selalu perlu) + retry terbatas dengan jitter + batas connection pool |
| Distributed tracing, SLA (0023) | Request melewati tiga service atau lebih dan kenaikan p99 tidak terjelaskan dari metrics dan logs | Monolith dengan satu database | Log terstruktur dengan request id + empat golden signals; satu atau dua SLO untuk endpoint terpenting |
Kalimat untuk interview: "Untuk skala ini cukup X; kalau metrik Y melewati Z, langkah berikutnya W." Komponen tanpa angka = over-engineering.
[1] PostgreSQL docs: Using EXPLAIN · [2] Azure Architecture Center: Cache-Aside pattern · [3] PostgreSQL docs: Hot Standby · [4] PostgreSQL docs: SELECT (locking clause, SKIP LOCKED) · [5] PostgreSQL docs: Table Partitioning · [6] Google SRE Book, bab 19: Load Balancing at the Frontend · [7] PostgreSQL docs: JSON Types · [8] Redis docs: Key eviction · [9] nginx: Module ngx_http_limit_req_module
Cheat sheet lain: angka dan estimasi · kerangka 5 langkah · Indeks