Lompat ke konten
17 menit bacamateri 15 dari 96

Batasi durasi request

Teruskan deadline lewat rantai pemanggilan dan bagi anggaran waktu ke tiap downstream, supaya request yang sudah ditinggal klien nggak terus menghabiskan sumber daya.

Ditinjau 26 Oktober 2026

Pertanyaan

Berapa lama sebuah request boleh hidup, dan bagaimana tiap layanan di rantai pemanggilan tahu sisa waktunya?

Selesai baca ini, kamu bisa menetapkan deadline di tepi sistem, meneruskannya ke downstream, dan membagi anggaran waktu dengan angka, bukan dengan tebakan.

Prasyarat dan contoh

Kamu paham komponen latensi request dari M02 (koneksi, antrean, pemrosesan) dan tahu bahwa p99 bisa jauh di atas rata-rata.

Contoh: klien mobile memanggil API POST /checkout dengan timeout 5 detik. API memanggil layanan stok (timeout default library: 30 detik), lalu layanan pembayaran (timeout default: 60 detik). Layanan pembayaran sedang lambat dan butuh 20 detik. Klien menyerah di detik ke-5, tapi API tetap menunggu, menahan thread, koneksi database, dan memori selama 20 detik lagi. Dengan 100 request per detik, dalam 20 detik ada 2.000 request yang menggantung walaupun nggak ada klien yang masih menunggu hasilnya. Pool koneksi habis, dan layanan yang tadinya hanya lambat jadi mati total.

Masalahnya bukan satu timeout yang terlalu besar. Masalahnya nggak ada yang memegang anggaran waktu untuk keseluruhan request.

Mekanisme

1. Tetapkan deadline di tepi. Deadline adalah titik waktu absolut ("sampai 12:00:05.000"), bukan durasi. Ditetapkan sekali di pintu masuk dari kebutuhan nyata: klien berhenti menunggu di detik 5, jadi kerja yang selesai setelah itu sia-sia. Sisihkan sedikit untuk perjalanan jaringan klien, misalnya deadline internal 4,5 detik.

2. Teruskan sebagai sisa waktu. Durasi harus dihitung ulang di tiap lompatan: jam di mesin berbeda nggak sinkron, jadi header yang membawa titik waktu absolut rawan salah. Teruskan sisa waktu (timeout-remaining: 3200ms) dan tiap layanan menghitung deadline lokalnya dari jam sendiri. gRPC melakukan ini otomatis lewat grpc-timeout. Untuk HTTP, kamu mendefinisikan header sendiri di kontrak.

3. Bagi anggaran. Misal deadline total 4.500 ms, dengan langkah berurutan:

| Langkah | p99 saat normal | Anggaran | | --- | --- | --- | | Validasi dan auth | 20 ms | 100 ms | | Layanan stok | 150 ms | 600 ms | | Layanan pembayaran | 1.200 ms | 2.500 ms | | Tulis database | 50 ms | 300 ms | | Cadangan untuk respons | | 1.000 ms |

Anggaran tiap langkah beberapa kali p99 normalnya, tapi jumlahnya nggak boleh melebihi deadline. Kalau kamu berniat retry satu langkah, anggarannya harus muat untuk kedua percobaan (lihat M03.04). Timeout per percobaan dibatasi oleh min(timeout_langkah, sisa_deadline).

4. Hentikan kerja yang sia-sia. Sebelum memulai langkah mahal, cek sisa waktu. Kalau sisanya kurang dari yang dibutuhkan langkah itu, kembalikan error sekarang (504 atau 503) daripada memulai pekerjaan yang pasti nggak terpakai. Batalkan pemanggilan downstream yang masih berjalan saat deadline lewat, dan lepaskan koneksi dan lock.

5. Pilih perilaku saat habis. Deadline lewat bukan berarti pekerjaan nggak terjadi: pembayaran bisa sudah diproses di provider walaupun kamu berhenti menunggu. Timeout itu "hasil nggak diketahui", bukan "gagal". Ini penyebab utama kenapa idempotensi dibutuhkan (M03.05).

Saat rancangan ini gagal

  • Timeout hilir lebih panjang dari hulu. Klien 5 detik, API 30 detik. Pekerjaan terus berjalan setelah klien pergi. Aturan: timeout selalu menurun makin ke dalam.
  • Deadline nggak diteruskan. Tiap layanan memakai timeout default miliknya. Layanan C masih bekerja untuk request yang sudah ditinggal A dua lompatan lalu.
  • Timeout terlalu ketat. Anggaran sama dengan p50. Request normal yang agak lambat gagal, dan klien yang retry menambah beban. Dasari angka pada p99 atau p99,9, bukan rata-rata.
  • Pembatalan nggak sampai ke sumber daya. Kode berhenti menunggu, tapi query database tetap berjalan 20 detik. Pastikan driver dan server mendukung pembatalan, dan atur statement_timeout.
  • Jam yang nggak sinkron. Deadline absolut dari mesin A dibaca mesin B yang selisih 800 ms. Teruskan sisa durasi, bukan stempel waktu.

Latihan

Deadline klien 3 detik. Alur: gateway (10 ms) lalu layanan A, yang memanggil layanan B lalu C secara berurutan, dan menulis ke database (40 ms). p99 normal: B 200 ms, C 400 ms. Layanan C punya timeout default 10 detik. Saat C melambat, trafik 200 request per detik.

  1. Berapa request yang bisa menggantung di C selama 10 detik?
  2. Susun anggaran untuk B dan C dengan deadline internal 2,7 detik.
  3. Bagaimana A berperilaku kalau B sudah memakai 2,4 detik?
Lihat pembahasan
  1. 200 request per detik kali 10 detik = sekitar 2.000 request menggantung di C, padahal klien berhenti menunggu di detik ke-3. Sekitar 70% dari antrean itu (detik 3 sampai 10) adalah kerja untuk klien yang sudah pergi.
  2. Sisihkan gateway 10 ms, database 40 ms dengan anggaran 200 ms, dan cadangan respons 300 ms. Sisa sekitar 2.190 ms untuk B dan C. Contoh: B 800 ms (4 kali p99), C 1.200 ms (3 kali p99). Jumlahnya 2.000 ms, di bawah sisa. Selalu pakai min(anggaran, sisa_deadline).
  3. Sisa waktu sekitar 300 ms, sedangkan C butuh p99 400 ms. A sebaiknya nggak memanggil C dan langsung mengembalikan 504. Memulai C hanya menambah beban ke layanan yang kemungkinan besar menyelesaikan pekerjaannya setelah klien pergi. Kalau C punya efek samping, hasilnya malah "terjadi tapi nggak ada yang tahu".

Dengan anggaran C 1,2 detik, antrean menggantung turun jadi 200 kali 1,2 = sekitar 240 request, kurang dari sepersepuluhnya.

Terkait

Catatan

  • Asumsi: rantai pemanggilan sinkron dan layanan yang bisa kamu ubah. Untuk layanan pihak ketiga, kamu hanya mengontrol sisi kamu.
  • Angka anggaran adalah contoh. Dasarnya harus p99 yang kamu ukur sendiri.
  • Yang nggak dibahas: deadline untuk pekerjaan asinkron (antrean) dan streaming panjang.
  • Terakhir ditinjau: 26 Oktober 2026.

Isi · System Design