Lesson 0022 · System Design · Modul Reliabilitas & API · ±20 menit

Timeout, retry + backoff, circuit breaker

Empat alat saat dependency lambat atau gagal: kapan menyerah, kapan mengulang, kapan berhenti mencoba, dan bagaimana mengurung kerusakan supaya tidak menjalar

Sebelumnya, di lesson 0021, Anda melindungi service dari client yang terlalu rajin. Lesson ini membahas sisi sebaliknya: service Anda menjadi client bagi database, payment gateway, atau service lain, dan dependency itu sedang bermasalah.

Target lesson ini: Anda bisa menjawab "Dependency Anda lambat. Apa yang Anda lakukan?" dalam 60 detik: timeout dari angka latency, retry hanya untuk operasi idempotent dengan exponential backoff + jitter, circuit breaker dengan tiga state-nya, dan bulkhead untuk mengurung kegagalan.

1. Timeout: menyerah tepat waktu

Panggilan tanpa timeout bisa menunggu selamanya. Dokumentasi gRPC menyebutnya langsung: secara default gRPC tidak memasang deadline, sehingga client bisa menunggu response "effectively forever"; selalu pasang deadline yang realistis. [1] Thread dan koneksi yang menunggu tidak bisa melayani request lain, jadi satu dependency yang macet bisa membuat seluruh service macet.

Berapa angkanya? AWS memulai dari metrik latency dependency: pilih tingkat false timeout yang dapat diterima (misalnya 0,1%), lalu pakai percentile yang sesuai di dependency itu (p99.9). [2] Ini menyambung ke percentile di lesson 0003: timeout yang baik diturunkan dari angka yang diukur, bukan ditebak.

2. Retry: hanya yang aman, dengan jeda yang makin panjang

Banyak kegagalan bersifat sementara (koneksi putus, server sedang restart), jadi mencoba lagi sering berhasil. Dua syaratnya:

Gagal→ tunggu acak 0–100 ms→ gagal → acak 0–200 ms→ gagal → acak 0–400 ms→ batas 3 percobaan: kembalikan error

Bahaya retry berlapis. Kalau request user melewati lima layer service dan setiap layer mencoba 3 kali, beban ke database saat ia mulai gagal naik 243 kali lipat (3⁵), sehingga database hampir tidak mungkin pulih. [2] Solusinya: retry di satu layer saja, dan pasang retry budget, misalnya maksimal 60 retry per menit per proses; kalau budget habis, request langsung gagal. [4]

Latihan 1 — Timeout dan retry

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

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

3. Circuit breaker: berhenti mencoba sementara

Retry membantu kalau gangguannya singkat. Kalau dependency mati sepuluh menit, retry hanya membuang waktu dan menambah beban. Circuit breaker adalah proxy di depan panggilan yang menghitung kegagalan dan punya tiga state [5]:

Closed Open Half-Open kegagalan melewati ambang timer habis uji berhasil uji gagal request lewat gagal cepat
Closed = normal, Open = jangan panggil dependency, Half-Open = uji kecil sebelum kembali normal.

4. Bulkhead: mengurung kerusakan

Nama pola ini diambil dari sekat lambung kapal: kalau lambung bocor, hanya satu ruang yang kebanjiran dan kapal tidak tenggelam. Di software, setiap dependency diberi sumber daya sendiri, misalnya connection pool per service yang dipanggil. Kalau satu service mulai gagal, yang habis hanya pool miliknya, dan panggilan ke service lain tetap berjalan. [6] Contoh: service rekomendasi yang lambat tidak boleh menghabiskan koneksi yang dibutuhkan checkout.

Latihan 2 — Circuit breaker dan bulkhead

5. Kapan tidak perlu

Timeout selalu perlu dan hampir gratis: satu baris konfigurasi di HTTP client, driver database, atau gRPC. Retry dengan backoff juga murah kalau library atau SDK sudah menyediakannya. Yang perlu dibenarkan dengan angka adalah dua sisanya.

Sinyal yang membenarkan circuit breaker dan bulkhead: dependency yang gagal pernah menyeret endpoint lain (p99 semua endpoint naik bersamaan saat satu dependency lambat), pool koneksi atau thread habis saat insiden, atau dependency eksternal dengan riwayat outage panjang. Monolith yang hanya memanggil satu database belum butuh keduanya: kalau database mati, tidak ada yang bisa diselamatkan dengan breaker.

Biaya dan risiko: ambang dan timer circuit breaker harus disetel; terlalu sensitif membuat breaker terbuka saat gangguan kecil dan menolak request yang sebenarnya bisa berhasil. Setiap instance punya state breaker sendiri, sehingga perilakunya sulit diuji. Bulkhead mengunci kapasitas per dependency, jadi sebagian pool menganggur saat traffic normal.

Alternatif lebih murah: timeout yang ketat + retry terbatas dengan jitter + batas ukuran connection pool. Kombinasi ini sudah mencegah sebagian besar cascading failure sebelum Anda butuh breaker.

Latihan lisan — Ucapkan dengan suara keras

Pasang timer dan jawab dengan suara keras, seolah sedang interview. Jangan buka jawaban contoh sebelum selesai. Soal ini juga ada di bank soal dan bisa ditarik review 0026.

"Service checkout memanggil payment gateway yang kadang lambat atau gagal. Bagaimana Anda membuat panggilan itu tahan gangguan tanpa memperparah masalah?" (60 detik)
Bandingkan dengan jawaban contoh dan checklist-nya

"Pertama timeout: tanpa timeout thread menunggu selamanya. Saya ambil dari latency payment gateway, misalnya sedikit di atas p99.9-nya. Kedua retry, tapi hanya untuk error sementara dan hanya karena request membawa idempotency key, karena POST pembayaran tidak idempotent dan timeout tidak berarti tagihan gagal. Retry pakai exponential backoff dengan jitter, misalnya 100, 200, 400 milidetik yang diacak, supaya client tidak mengulang serentak, dan dibatasi tiga kali di satu layer saja, karena retry di lima layer bisa melipatgandakan beban 243 kali. Ketiga circuit breaker: kalau error rate melewati ambang, breaker Open dan checkout langsung gagal cepat atau menawarkan 'coba lagi nanti', lalu Half-Open untuk uji. Terakhir bulkhead: pool koneksi terpisah untuk payment supaya endpoint lain tidak ikut macet. Timeout dan retry terbatas itu murah dan wajib; breaker dan bulkhead saya tambah kalau dependency ini terbukti bisa menyeret seluruh service."

Checklist: apakah Anda menyebut…

  • timeout berdasarkan latency dependency (misalnya p99/p99.9)?
  • retry hanya untuk operasi idempotent atau dengan idempotency key?
  • exponential backoff dengan jitter supaya retry tidak serentak?
  • batas retry di satu layer, karena retry berlapis saling mengalikan?
  • circuit breaker: Closed, Open, Half-Open untuk gagal cepat?
  • bulkhead atau pool terpisah, ditambahkan karena ada risiko yang terukur?

Lulus jika minimal 4 dari 6.

Bacaan lanjutan (opsional, tidak dihitung dalam durasi) AWS Builders' Library: Timeouts, retries, and backoff with jitter (Marc Brooker), ±15 menit.

Berikutnya, lesson 0023: semua keputusan di atas butuh angka (p99.9, error rate, ambang). Dari mana angka itu datang, dan berapa banyak kegagalan yang boleh terjadi?

Istilah: GLOSSARY.md (bagian "Reliabilitas & API") · Indeks: semua lesson System Design

Ada yang belum jelas? Tanyakan ke saya (agent) di terminal, misalnya: "Apa beda full jitter dan equal jitter?", "Di layer mana sebaiknya retry dipasang kalau ada API gateway?"

Sumber: [1] gRPC: Deadlines · [2] AWS Builders' Library: Timeouts, retries, and backoff with jitter · [3] RFC 9110 HTTP Semantics, §9.2.2 Idempotent Methods · [4] Google SRE Book, ch. 22: Addressing Cascading Failures · [5] Azure Architecture Center: Circuit Breaker pattern · [6] Azure Architecture Center: Bulkhead pattern