Load Shedding Menjaga Pekerjaan Berguna Saat Saturasi

Sebuah service memiliki kapasitas terbatas untuk menyelesaikan pekerjaan per satuan waktu. Ketika beban yang masuk melewati kapasitas tersebut, menerima setiap request tidak menambah kapasitas. Keputusan itu justru menambah waktu tunggu, memakai memory dan connection slot, memperpanjang deadline, serta dapat membiarkan pekerjaan mahal terus berjalan setelah caller berhenti menunggu.

Load shedding membuat admission menjadi eksplisit. Pekerjaan yang tidak dapat diproses service dalam operating envelope-nya ditolak lebih awal agar pekerjaan yang diterima tetap memiliki peluang realistis untuk selesai.

offered load:  14.000 req/s
safe capacity: 10.000 req/s

admit:          10.000 req/s
shed:            4.000 req/s

Nilai utamanya bukan pada rejection itu sendiri. Nilainya terletak pada kemampuan menjaga overload tetap terbatas, alih-alih membiarkannya menyebar melalui queue, dependency, dan retry loop.

Saturasi mengubah nilai dari keputusan menerima pekerjaan

Pada utilization rendah, menerima satu request tambahan biasanya murah. Mendekati saturasi, keputusan yang sama dapat menambah queueing delay bagi request yang sudah berada di dalam sistem.

Misalkan worker pool dapat menjalankan 100 operasi secara concurrent dan semua slot sedang terpakai. Request baru yang ditempatkan di belakang ribuan request lain mungkin melewati deadline sebelum eksekusi dimulai. Menahannya tetap memakai memory queue, bookkeeping, dan sering kali sebuah connection. Jika caller melakukan retry, sistem dapat menerima salinan lain sebelum request pertama keluar dari queue.

Admission gate dapat menolak request tersebut ketika biayanya masih kecil.

request
   |
   v
capacity signal
   |
   +-- capacity tersedia --> execute
   |
   `-- saturated ---------> reject lebih awal

Dengan demikian, kekurangan kapasitas tidak berubah menjadi inventory pekerjaan stale yang terus bertambah.

Admission signal harus mengikuti resource yang langka

Shedding policy hanya sebaik signal yang mengendalikannya. CPU utilization cocok untuk pekerjaan CPU-bound, tetapi nilainya dapat kecil pada service yang tertahan oleh database connection. Queue depth memperlihatkan pekerjaan yang menunggu, tetapi queue pendek pun dapat bermasalah ketika setiap operasi memegang downstream permit yang langka dalam waktu lama.

Signal yang berguna mencakup active concurrency, queue age, queue depth, event-loop lag, memory pressure, connection-pool occupancy, dependency saturation, dan recent completion rate. Pilih signal yang mengikuti resource pembatas useful throughput.

Concurrency gate sederhana dapat memadai ketika biaya setiap request yang diterima relatif serupa.

if active_requests >= concurrency_limit:
    reject()
else:
    active_requests += 1
    execute()

Mixed workload memerlukan kontrol lebih rinci. Satu report export dapat memakai CPU, memory, atau database time jauh lebih besar daripada satu metadata lookup. Satu request count tidak menangkap perbedaan tersebut.

Rejection awal lebih murah daripada timeout terlambat

Request yang ditolak saat admission hanya memakai sedikit kapasitas service. Request yang menunggu 800 ms, mulai bekerja, memanggil dua dependency, lalu mencapai deadline 1 detik sudah memakai kapasitas tanpa menghasilkan outcome yang dapat dipakai.

Perbedaan ini penting saat overload karena kegagalan terlambat mengurangi kapasitas yang tersedia bagi request yang masih mungkin selesai.

early shed:
arrival -> reject

late failure:
arrival -> queue -> execute -> dependency -> deadline

Service karena itu perlu membandingkan perkiraan waktu tunggu dan waktu eksekusi dengan sisa deadline request. Pekerjaan tanpa completion window yang masuk akal merupakan kandidat kuat untuk ditolak.

Pendekatan ini juga menyelaraskan failure dengan budget caller. Overload response yang cepat memberi upstream component waktu untuk memilih fallback, mengalihkan request, atau mengembalikan error eksplisit daripada menunggu timeout.

Tidak semua pekerjaan memiliki prioritas yang sama

Melakukan shedding dengan probabilitas yang sama untuk setiap request memang sederhana, tetapi banyak sistem memiliki kelas pekerjaan dengan nilai operasional berbeda. Health traffic, interactive read, background refresh, batch export, dan speculative request tidak selalu layak memakai admission policy yang sama.

Capacity budget terpisah membuat perbedaan itu konkret.

interactive pool:  700 permit
background pool:   200 permit
reserved pool:     100 permit

Reservation mencegah traffic berprioritas rendah menghabiskan semua slot sebelum pekerjaan kritis tiba. Meminjam kapasitas yang sedang tidak dipakai antarkelas dapat meningkatkan utilization, selama sistem dapat menarik kembali atau menghentikan peminjaman ketika reserved class mulai aktif.

Priority bukan alasan untuk membuat queue tanpa batas. Request berprioritas tinggi tetap dapat datang terlalu terlambat untuk selesai. Priority memilih di antara pekerjaan yang bersaing; capacity limit tetap berlaku.

Retry policy dan shedding policy membentuk satu feedback system

Overload response sering membuat client melakukan retry. Jika setiap request yang ditolak langsung mendapat respons dan setiap caller langsung melakukan retry, shedding dapat mengurangi pekerjaan di dalam service sekaligus meningkatkan offered load pada boundary.

Client memerlukan retry count yang terbatas, backoff, jitter, serta deadline yang tetap berlaku lintas-attempt. Server dapat mengirim retry hint jika protocol mendukungnya, tetapi hint tersebut perlu mencerminkan pola recovery yang nyata, bukan nilai optimistis yang selalu sama.

attempt 1 -> overload response
             |
             +-- backoff + jitter
             |
attempt 2 ---+

Admission metric perlu menghitung logical traffic secara terpisah dari retry attempt. Rasio attempt terhadap logical operation yang meningkat dapat menunjukkan retry storm meski completed throughput terlihat stabil.

Shedding ditempatkan dekat resource yang dilindungi

Gateway dapat menolak traffic berlebih sebelum mencapai application fleet sehingga menghemat network dan process overhead. Application tetap memerlukan perlindungan lokal karena kapasitas agregat di gateway tidak menunjukkan setiap bottleneck lokal.

Database client pool, misalnya, dapat jenuh saat CPU masih tersedia. Per-process concurrency limiter dapat melindungi pool meski global request rate terlihat normal.

Layered admission karena itu umum digunakan:

edge limit
   |
service concurrency limit
   |
dependency-specific limit
   |
database pool

Setiap layer perlu melindungi resource konkret dan mengekspos alasan rejection. Menumpuk limit arbitrer tanpa resource ownership membuat incident lebih sulit didiagnosis dan dapat menyia-nyiakan kapasitas.

Adaptive limit memerlukan control behavior yang stabil

Static limit mudah diprediksi tetapi dapat menjadi stale ketika instance size, dependency latency, atau workload cost berubah. Adaptive concurrency controller dapat menggeser admission limit berdasarkan latency atau queueing signal yang diamati.

Controller itu sendiri menjadi bagian dari feedback loop sistem. Perubahan limit yang besar dan cepat dapat berosilasi: limit tinggi menciptakan queueing, controller memangkasnya secara tajam, latency turun, lalu controller menaikkan limit terlalu agresif.

Controller praktis memakai perubahan terbatas, smoothing, minimum sample requirement, serta floor dan ceiling eksplisit. Controller juga perlu membedakan overload dari kenaikan latency yang berasal dari sebab lain. Mengubah admission berdasarkan signal yang noisy dapat membuat incident pada external dependency lebih sulit dibatasi.

Adaptive control perlu memiliki safe failure mode. Jika telemetry hilang, service memerlukan fallback limit yang jelas, bukan admission tanpa batas.

Overload response merupakan bagian dari API contract

Service HTTP umumnya memakai 429 Too Many Requests untuk rate limit yang spesifik terhadap caller atau policy, sedangkan 503 Service Unavailable dipakai untuk masalah kapasitas service yang bersifat sementara. Semantik tepatnya bergantung pada API dan lokasi admission.

Status code saja tidak cukup. Operator perlu mengetahui gate mana yang menolak request, capacity class mana yang habis, serta apakah pekerjaan sempat mencapai downstream system.

Telemetry record yang berguna dapat berbentuk:

admission_result=shed
gate=database_concurrency
class=interactive
active=240
limit=240
remaining_deadline_ms=73

Metric perlu mencakup admission rate, shed rate berdasarkan alasan dan kelas, completion rate, queue age, active concurrency, deadline expiry, retry amplification, serta resource utilization. Shedding yang bekerja baik sering terlihat sebagai completed throughput yang stabil saat offered traffic dan rejected traffic meningkat.

Overload yang stabil merupakan operating mode yang disengaja

Service tidak dapat menjamin completion untuk offered load tanpa batas. Service dapat menetapkan perilaku ketika demand melewati safe capacity.

Tanpa admission control, overload sering muncul sebagai queue yang terus tumbuh, tail latency yang meningkat, memory pressure, timeout cascade, serta retry yang menambah traffic. Dengan bounded queue dan shedding eksplisit, excess demand terlihat dekat boundary tempat sistem masih dapat menolaknya dengan biaya rendah.

Target desainnya adalah region yang terkendali: pekerjaan yang diterima sesuai capacity budget, excess work mendapat respons cepat dan observable, priority rule menjaga kapasitas terpilih, serta retry behavior tidak langsung menciptakan kembali beban yang baru ditolak.

Operating mode tersebut mengubah saturasi dari masalah akumulasi yang tidak terkendali menjadi keputusan kapasitas yang eksplisit.