Bounded Queue Mengubah Overload Menjadi Backpressure yang Eksplisit

Queue dapat menyerap perbedaan singkat antara arrival rate dan processing rate. Buffer semacam ini berguna ketika lonjakan hanya berlangsung sementara. Risikonya muncul saat queue tidak memiliki batas yang bermakna: overload berkepanjangan tidak terlihat sebagai kegagalan admission, melainkan sebagai backlog yang terus tumbuh, konsumsi memori yang meningkat, dan request yang selesai setelah latency budget-nya lewat.

Bounded queue mengubah kontrak tersebut. Setelah kapasitas habis, producer harus menunggu, menerima penolakan, membuang work tertentu, atau mengarahkannya ke tempat lain. Overload tidak lagi tersembunyi di dalam buffer yang terus membesar.

Kapasitas queue adalah keputusan latency

Misalkan worker memproses 500 job per detik dan queue dapat menampung 5.000 job. Jika worker terus berada pada kondisi saturated, job yang masuk di bagian belakang queue penuh sudah memiliki sekitar sepuluh detik work di depannya, bahkan sebelum service time miliknya dihitung.

Delay sebenarnya bergantung pada distribusi service time dan concurrency, tetapi hubungannya tetap sama: kapasitas queue yang lebih besar mengizinkan waktu tunggu lebih panjang. Karena itu, kapasitas tidak tepat jika ditentukan hanya dari jumlah memori yang tersedia.

Untuk work yang sensitif terhadap latency, queue kecil dapat lebih sehat daripada queue besar. Menolak kelebihan work dekat titik admission dapat mempertahankan layanan yang berguna bagi request yang masih mungkin selesai sebelum deadline.

arrival rate > service rate
        |
        v
queue tumbuh sampai capacity
        |
        v
admission policy aktif

Kapasitas menandai titik ketika sistem berhenti mengubah overload menjadi tambahan waktu tunggu.

Unbounded queue menunda sinyal kegagalan

API yang menerima work ke in-memory queue yang praktis tidak terbatas dapat terlihat sehat ketika downstream worker sebenarnya sudah saturated. Producer terus menerima hasil enqueue yang sukses, padahal completion latency sedang memburuk.

Memory pressure kemudian berubah menjadi overload controller yang tidak disengaja. Entry queue, referensi payload, retry metadata, tracing context, dan object terkait terus terakumulasi sampai garbage collection, allocator pressure, swapping, atau out-of-memory termination menghasilkan kegagalan yang terlihat.

Control loop semacam ini buruk karena titik kegagalan terikat pada memori process, bukan pada kontrak service. Bounded queue memindahkan keputusan ke tempat yang disengaja sehingga software dapat menerapkan kebijakan yang sudah ditentukan.

Perilaku saat queue penuh adalah bagian dari API

Bounded queue belum lengkap tanpa semantics untuk kondisi penuh. Kebijakan umum meliputi memblokir producer, mengembalikan overload error, membuang work tertentu, atau mengganti work lama ketika hanya state terbaru yang memiliki nilai.

Setiap kebijakan cocok untuk workload berbeda.

Blocking dapat meneruskan pressure secara alami melalui pipeline sinkron, tetapi hanya jika menahan producer tidak ikut menahan resource langka yang diperlukan agar sistem dapat maju. Request handler yang melakukan block sambil mempertahankan database connection, misalnya, dapat memindahkan saturation ke connection pool.

Rejection sering lebih jelas untuk request-response service. Caller menerima hasil overload secara eksplisit dan dapat menerapkan retry policy, memilih replica lain, atau berhenti menghasilkan work opsional. Retry tetap memerlukan rate control; retry langsung dapat memperbesar overload yang memicu rejection tadi.

Dropping dapat benar untuk telemetry sample, refresh hint, atau state yang dapat digabung ketika setiap item perantara tidak memiliki nilai independen yang besar. Kebijakan ini bukan pengganti umum untuk durable delivery.

Backpressure harus melewati asynchronous boundary

Kegagalan yang umum terjadi ketika satu stage yang bounded meneruskan work ke komponen lain melalui handoff yang unbounded. Queue yang terlihat tetap kecil, tetapi backlog hanya berpindah tempat.

producer
   |
[queue A: 100]
   |
worker
   |
[queue B: unbounded]
   |
remote service

Queue A tidak dapat melindungi process jika worker mengosongkannya dengan cepat ke queue B sementara remote service lambat. Backpressure yang efektif harus mencapai titik admission melalui setiap asynchronous boundary yang dapat menumpuk work.

Tidak semua komponen harus memakai mekanisme yang sama. Semaphore dapat membatasi concurrent call, broker dapat menerapkan partition atau retention limit, dan protocol dapat menyediakan flow-control window. Properti pentingnya adalah outstanding work memiliki budget terbatas dan kondisi habisnya budget dapat diamati.

Timeout tidak menggantikan capacity limit

Timeout membatasi berapa lama sebuah operasi masih dianggap berguna. Timeout tidak selalu membatasi berapa banyak operasi dengan timeout yang dapat masuk queue pada saat bersamaan.

Jika 100.000 request masuk ke queue dengan deadline lima detik, worker dapat menghabiskan banyak kerja untuk mengambil entry yang deadline-nya sudah lewat. Queue juga tetap menyimpan entry tersebut sampai cancellation diteruskan atau stale work dibuang.

Capacity dan deadline menyelesaikan bagian masalah yang berbeda. Capacity membatasi backlog yang diterima. Deadline membatasi useful lifetime. Sistem yang membawa deadline ke dalam queue dapat membuang work yang kedaluwarsa sebelum processing mahal dimulai, tetapi admission limit tetap diperlukan agar akumulasi tidak menjadi arbitrer.

Metric queue memerlukan depth dan age

Queue depth saja dapat menyesatkan. Depth 200 mungkin tidak bermasalah untuk job yang selesai dalam hitungan milidetik, tetapi sangat berat untuk job yang memerlukan beberapa detik.

Signal yang berguna meliputi:

current queue depth
queue capacity
oldest queued item age
enqueue rejection count
time spent waiting for admission
service time after dequeue
expired or cancelled items removed

Oldest-item age sangat berguna karena menghubungkan backlog langsung dengan waktu tunggu. Queue yang baru setengah penuh tetap dapat menyimpan stale work setelah worker melambat atau sebagian worker tidak tersedia.

Rejection rate juga memerlukan konteks. Rejection yang tidak nol bukan otomatis defect. Pada load shedding yang disengaja, rejection dapat menjadi mekanisme yang menjaga bounded latency untuk work yang diterima. Pertanyaan operasionalnya adalah apakah capacity dan overload policy sesuai dengan service objective.

Capacity perlu mengikuti resource budget

Batas queue paling berguna ketika berhubungan dengan constraint konkret: waktu tunggu yang dapat diterima, memori per item, downstream concurrency, broker retention, atau jumlah maksimum stale work.

Untuk service yang stabil, Little’s Law memberi hubungan yang berguna antara rata-rata jumlah item di dalam sistem, throughput, dan rata-rata waktu di dalam sistem. Hubungan ini tidak menjadikan queue sizing satu formula universal, terutama saat arrival bersifat bursty dan service time memiliki heavy tail, tetapi mencegah capacity diperlakukan sebagai angka besar yang dipilih tanpa dasar.

Load test perlu mencakup overload berkepanjangan, bukan hanya peak singkat. Desain yang tetap baik selama burst sepuluh detik masih dapat gagal setelah beberapa menit jika arrival rate terus berada di atas service capacity.

Queue penuh adalah state yang terkendali

Saturation tidak selalu dapat dicegah. Pilihan engineering-nya adalah apakah saturation muncul pada boundary yang terkendali atau baru terlihat kemudian sebagai memory exhaustion dan latency ekstrem.

Bounded queue membuat resource budget menjadi eksplisit. Kondisi penuh memaksa sistem menetapkan langkah berikutnya: menunggu, menolak, membuang, atau mengalihkan work. Keputusan tersebut terlihat, dapat diukur, dan dapat diuji.

Queue tetap meredam burst sementara, tetapi tidak lagi menjanjikan waktu tunggu tanpa batas. Setelah buffer budget habis, pressure kembali ke bagian sistem yang dapat mengambil keputusan admission.