Batas Concurrency Adaptif Mengikuti Kapasitas Service yang Tersedia

Batas concurrency tetap mudah dioperasikan ketika kapasitas service stabil. Sistem nyata jarang berada dalam satu kondisi operasi. Contention database, cache hit rate, campuran request, latency downstream, ketersediaan CPU, dan perubahan deployment dapat menggeser jumlah pekerjaan yang mampu ditangani service secara bersamaan.

Kontrol concurrency adaptif memperlakukan batas in-flight sebagai variabel kontrol. Limiter menerima pekerjaan sampai ceiling saat ini, mengamati perilaku service, lalu menyesuaikan ceiling tersebut. Sasarannya bukan concurrency maksimum, melainkan concurrency yang cukup untuk memakai kapasitas tersedia tanpa membiarkan antrean tumbuh jauh melewati area operasi yang berguna.

admitted in flight <= current_limit

completion samples
      |
      v
latency / saturation signal
      |
      v
limit adjustment

Feedback loop membuat mekanisme ini berbeda dari semaphore statis. Semaphore menegakkan batas yang dikonfigurasi; limiter adaptif juga mengubah batas tersebut berdasarkan evidence runtime.

Concurrency bukan throughput

Concurrency menghitung operasi yang sudah dimulai tetapi belum selesai. Throughput menghitung pekerjaan yang selesai per satuan waktu. Menaikkan concurrency dapat meningkatkan throughput selama service masih memiliki kapasitas kosong, tetapi hubungan itu berhenti menguntungkan setelah bottleneck mencapai saturasi.

Misalkan sebuah dependency menyelesaikan request dalam sekitar 20 ms pada beban ringan. Dua puluh request concurrent, dalam steady state yang ideal, dapat mendukung sekitar 1.000 completion per detik. Jika contention mendorong service time menjadi 80 ms, concurrency yang sama hanya mendukung sekitar 250 completion per detik.

Little’s Law memberi hubungan dasar untuk sistem yang stabil:

L = lambda * W

L       rata-rata pekerjaan dalam sistem
lambda  rata-rata completion rate
W       rata-rata waktu dalam sistem

Persamaan ini tidak menetapkan batas yang aman. Persamaan tersebut menunjukkan bahwa concurrency, throughput, dan waktu saling terkait. Batas yang mengabaikan perubahan service time dapat menciptakan antrean besar tanpa menghasilkan kenaikan useful throughput yang sebanding.

Latency minimum dapat menjadi sinyal referensi

Banyak skema adaptif membandingkan latency terbaru dengan referensi latency yang lebih rendah saat service menerima beban lebih ringan. Selisihnya menjadi evidence adanya queueing atau contention.

baseline latency:  18 ms
recent latency:    21 ms  -> selisih kecil
recent latency:    65 ms  -> selisih besar

Baseline harus dikelola dengan hati-hati. Nilai tersebut bukan konstanta fisik permanen. Rilis software, campuran request yang berbeda, migrasi database, atau penempatan infrastructure dapat mengubah latency terbaik yang dapat dicapai. Mempertahankan minimum lama selamanya dapat membuat perilaku sehat terlihat overloaded.

Controller praktis karena itu memerlukan policy eksplisit untuk memperbarui reference window. Sebagian desain secara berkala melakukan probe pada concurrency lebih rendah; desain lain menua-kan sample lama atau mempertahankan rolling estimate. Algoritma persisnya tidak sepenting semantic referensi yang ditetapkan secara sengaja.

Latency juga memerlukan measurement boundary yang jelas. Latency yang diamati client mencakup network dan upstream queueing yang mungkin tidak dikendalikan service. Server processing latency tidak mencakup waktu yang sudah dihabiskan menunggu sebelum admission. Sinyal yang dipilih sebaiknya sesuai dengan resource boundary yang hendak dilindungi limiter.

Pertumbuhan antrean adalah kondisi yang perlu dihindari

Ketika arrival rate melampaui completion capacity, pekerjaan tertunda akan menumpuk. Jika service menerima semuanya, latency dapat naik jauh sebelum error muncul.

arrival > completion
       |
       v
+---------------+
| growing queue |
+---------------+
       |
       v
higher latency, timeouts, retries

Concurrency limiter menempatkan waiting boundary sebelum pekerjaan mahal dimulai. Setelah in-flight budget habis, pekerjaan berlebih dapat ditolak, dimasukkan sebentar ke antrean dengan policy terpisah yang bounded, atau ditangani oleh overload path eksplisit lain.

Posisi boundary tersebut penting. Limiter yang berada setelah database connection langka sudah diperoleh tidak dapat melindungi connection pool dari request yang diterima. Admission sebaiknya terjadi sebelum resource yang saturasinya mengendalikan limit, selama arsitektur memungkinkan.

Perubahan limit memerlukan damping

Controller yang bereaksi agresif terhadap setiap sample dapat berosilasi. Lonjakan latency singkat menurunkan limit, load yang lebih rendah segera memperbaiki latency, kemudian controller menaikkan limit terlalu jauh dan siklus berulang.

Adjustment policy lazimnya memisahkan perilaku naik dan turun. Kapasitas dapat dieksplorasi secara bertahap, sedangkan overload memicu pengurangan yang lebih cepat.

healthy sample  -> increase a little
overload signal -> decrease more decisively

Pola ini menyerupai additive-increase dan multiplicative-decrease control, walau algoritma produksi bervariasi. Properti yang berguna adalah asimetri: pencarian spare capacity dapat dilakukan hati-hati, sedangkan keluar dari kondisi overloaded mungkin memerlukan respons lebih kuat.

Sample window juga mencegah satu completion mendominasi controller. Window yang terlalu pendek mengikuti noise; window yang terlalu panjang lambat merespons perubahan kapasitas nyata. Burstiness workload dan service time normal menentukan skala waktu yang relevan.

Kelas request dapat memerlukan limit terpisah

Satu concurrency slot bermakna ketika operasi yang diterima memiliki resource cost yang kurang lebih sebanding. Metadata lookup dan request pembuatan laporan dapat menahan CPU, memory, database connection, atau kapasitas downstream selama durasi yang sangat berbeda.

Satu limit bersama dapat membuat pekerjaan mahal menggeser pekerjaan murah. Pemisahan berdasarkan route, tenant, priority, atau resource class dapat membuat admission boundary lebih representatif:

interactive reads -> limiter A
batch exports      -> limiter B
background sync    -> limiter C

Limit terpisah memiliki biaya. Setiap controller menerima lebih sedikit sample dan setiap boundary memerlukan traffic yang cukup agar adaptasi dapat berjalan stabil. Terlalu banyak controller yang sangat granular dapat mengubah variasi traffic alami menjadi keputusan kontrol yang noisy.

Weighted concurrency merupakan pilihan lain, dengan operasi mahal memakai lebih dari satu unit. Static weight tetap berupa estimasi, sehingga telemetry masih perlu menunjukkan apakah resource yang dilindungi benar-benar berada dalam rentang operasi yang berguna.

Admission terdistribusi mengubah masalah kontrol

Limiter adaptif process-local hanya mengamati pekerjaan yang ditangani process tersebut. Dengan sepuluh replica, sepuluh limit independen dapat menerima aggregate concurrency jauh lebih besar daripada yang mampu ditanggung shared database.

Kontrol per-instance cocok ketika kapasitas ikut bertambah bersama setiap instance, seperti CPU lokal. Pendekatan ini kurang langsung untuk dependency bersama dengan kapasitas tetap. Pilihannya mencakup coordinated global limiter, alokasi per-instance dari global budget, atau local controller dengan saturation signal yang mencerminkan shared dependency.

Setiap pilihan mengubah failure behavior. Central admission service dapat menegakkan aggregate bound yang lebih ketat, tetapi menambah coordination latency dan dependency availability lain. Partitioned budget mengurangi koordinasi tetapi dapat meninggalkan kapasitas pada replica yang sepi. Local feedback murah, tetapi dapat konvergen tidak merata ketika distribusi traffic timpang.

Resource yang dilindungi sebaiknya menentukan scope limit.

Retry harus tetap berada di luar feedback trap

Pekerjaan yang ditolak sering membuat caller melakukan retry. Retry langsung dapat meningkatkan offered load tepat ketika limiter sedang mengurangi admission.

Overload response karena itu sebaiknya dipasangkan dengan bounded retry behavior, backoff, dan jitter ketika retry memang valid. Caller juga memerlukan deadline agar retry tertunda tidak berlanjut setelah operasi kehilangan nilainya.

Telemetry perlu membedakan original attempt dari retry serta mengekspos limit yang dikonfigurasi atau adaptif bersama jumlah in-flight yang teramati:

concurrency_limit=84
in_flight=84
admission=rejected
recent_latency_ms=47
reference_latency_ms=19
attempt=retry

Tanpa field tersebut, limit yang menurun dapat terlihat seperti throughput regression tanpa sebab, padahal itu merupakan respons yang disengaja terhadap saturasi.

Guardrail menjaga adaptasi tetap di dalam batas aman

Feedback tidak seharusnya memiliki wewenang tanpa batas. Minimum limit mempertahankan sedikit traffic service dan measurement. Maximum limit mencegah sample optimistis memperluas concurrency melewati constraint infrastructure yang sudah diketahui.

min_limit <= adaptive_limit <= max_limit

Startup behavior juga memerlukan policy eksplisit. Memulai dari maksimum dapat menciptakan overload burst sebelum controller memiliki cukup sample. Memulai terlalu rendah dapat membuat recovery terlalu lambat. Initial value konservatif yang didukung data operasi sebelumnya biasanya lebih mudah dianalisis dibanding kedua ekstrem tersebut.

Controller state juga dapat memerlukan reset rule. Periode idle panjang, deployment ke hardware berbeda, atau perubahan besar pada dependency dapat membuat sample lama menjadi evidence yang buruk untuk periode operasi berikutnya.

Kontrol concurrency adaptif paling efektif ketika feedback boundary sesuai dengan resource yang dilindungi. Limit kemudian menjadi estimasi runtime atas useful parallelism, bukan tebakan tetap. Reference signal yang stabil, adjustment yang teredam, retry yang bounded, dan guardrail eksplisit menjaga estimasi tersebut agar tidak berubah menjadi sumber instabilitas baru.