Hedged Request Menukar Kerja Duplikat dengan Tail Latency yang Lebih Rendah

Sebagian besar request dapat selesai cepat sementara sebagian kecil memerlukan waktu jauh lebih lama. Worker yang sibuk, antrean jaringan sesaat, cache entry yang dingin, garbage collection, storage contention, atau gangguan lokal lain dapat membuat satu attempt jauh melampaui median. Pada skala besar, outlier tersebut terlihat pada latency p95, p99, dan persentil yang lebih tinggi meski rata-rata service time tetap sehat.

Hedged request mengirim attempt tambahan setelah request awal belum selesai selama jeda yang telah ditentukan. Kedua attempt mewakili operasi logis yang sama. Caller menerima hasil valid pertama lalu membatalkan atau mengabaikan attempt yang tersisa.

Mekanisme ini membayar kerja tambahan untuk mengurangi paparan terhadap jalur eksekusi yang kebetulan sangat lambat. Pertukaran tersebut dapat berguna untuk read yang idempotent ketika kapasitas cadangan tersedia. Jika diterapkan tanpa batas, hedge justru dapat memperbesar load saat service sedang kesulitan.

Hedge memakai jeda, bukan duplikasi langsung

Mengirim dua salinan setiap request pada saat yang sama dapat menurunkan latency, tetapi request multiplier juga mendekati 2x. Hedging biasanya menunggu sebelum membuat attempt kedua.

Kebijakan sederhana dapat berbentuk seperti ini:

primary = send(request)

wait until:
    primary completes
    or hedge_delay expires

if primary completed:
    return primary.result

hedge = send(request)

result = first_success(primary, hedge)
cancel_other_attempt()
return result

Jeda merupakan bagian utama desain. Request yang selesai dalam rentang normal hanya memakai satu attempt. Hanya request yang melewati jeda yang memakai hedge.

Misalnya, hedge delay ditempatkan di sekitar p95 terbaru untuk operasi tersebut. Secara kasar, hanya bagian request paling lambat yang memenuhi syarat untuk attempt kedua, bukan seluruh workload. Proporsi sebenarnya bergantung pada distribusi latency saat itu, presisi timer, retry, queueing, dan cara threshold dipelihara.

Hedge berbeda dari timeout. Timeout memberi batas atas waktu tunggu dan dapat menghentikan sebuah attempt. Hedge memulai attempt lain ketika attempt pertama masih dapat berhasil. Keduanya dapat dipakai bersama, tetapi mengendalikan bagian berbeda dari lifecycle request.

Tail latency bergantung pada korelasi sumber keterlambatan

Hedge membantu ketika attempt awal dan duplikat kecil kemungkinannya terkena sumber keterlambatan yang sama. Jika satu worker sedang pause sementara worker lain sehat, mengarahkan hedge ke worker berbeda dapat melewati pause tersebut. Jika semua replica menunggu database yang sama dan sudah saturated, duplikat kemungkinan bertemu bottleneck yang sama.

Karena itu, korelasi lebih penting daripada sekadar jumlah attempt.

Jalur hedge yang baik berusaha membedakan kondisi yang dapat menghasilkan outlier:

  • replica berbeda jika routing mengizinkannya;
  • connection terpisah jika satu connection dapat mengalami head-of-line blocking;
  • worker yang dijadwalkan secara independen;
  • replica dengan data dan consistency semantics yang setara.

Diversifikasi tidak boleh diam-diam mengubah correctness. Read dari replica berbeda hanya aman jika replica tersebut memenuhi consistency requirement operasi. Response stale yang lebih cepat tetap salah ketika state terbaru diperlukan.

Hedging juga tidak dapat memperbaiki kerja mahal yang deterministik. Jika setiap attempt harus memindai dataset besar yang sama atau menunggu serialized critical section yang sama, duplikasi hanya menambah biaya tanpa menyediakan jalur independen menuju penyelesaian.

Hasil pertama belum tentu hasil pertama yang dapat diterima

“First wins” merupakan ringkasan praktis, tetapi logic produksi biasanya membutuhkan aturan yang lebih presisi.

Jika primary mengembalikan transport error dengan cepat sementara hedge masih berjalan, langsung mengembalikan error tersebut dapat membuang response sukses yang tinggal beberapa milidetik lagi. Caller dapat memilih hasil sukses pertama sambil mempertahankan aturan yang jelas untuk terminal error.

Kebijakannya dapat menetapkan:

success + pending       -> return success, cancel pending
success + error         -> return success
terminal error + pending -> wait according to policy
two terminal errors     -> return selected error
deadline reached        -> stop all attempts

Klasifikasi error merupakan bagian dari contract. Authentication failure, malformed request, atau application-level validation error umumnya tidak menjadi lebih sehat karena duplikasi. Transient transport failure dapat menjadi alasan untuk menunggu attempt lain yang sudah berjalan.

Caller juga memerlukan satu deadline keseluruhan. Memberikan full timeout baru kepada setiap hedge dapat memperpanjang total latency melewati budget request logis. Setiap attempt sebaiknya mewarisi sisa waktu dari deadline request yang sama.

Cancellation hanya menghemat kapasitas jika benar-benar diteruskan

Setelah satu attempt berhasil, attempt lain tidak lagi bernilai bagi caller. Cancellation yang cepat dapat mengurangi CPU, network traffic, database work, dan connection occupancy yang terbuang.

Cancellation tidak otomatis berarti kerja berhenti. Client library dapat membatalkan future ketika byte sudah dikirim. Server mungkin telah memulai query yang tidak mengamati client disconnect. Database downstream dapat terus mengeksekusi setelah aplikasi meninggalkan hasilnya.

Biaya efektif hedging bergantung pada seberapa jauh cancellation diteruskan melalui stack.

Instrumentation setidaknya perlu membedakan:

  • hedge yang dikirim;
  • hedge yang menghasilkan response pemenang;
  • losing attempt yang dibatalkan sebelum dispatch;
  • losing attempt yang dibatalkan saat eksekusi;
  • losing attempt yang tetap berjalan sampai selesai;
  • tambahan downstream request dan service time.

Hedge-win rate yang tinggi dapat menandakan perlindungan yang berguna dari outlier, tetapi juga dapat menunjukkan primary path yang rutin lambat. Hedging tidak seharusnya menutupi masalah capacity atau routing yang menetap.

Duplicate write membutuhkan semantics yang lebih kuat

Operasi read-only merupakan kandidat paling sederhana karena eksekusi duplikat biasanya tidak memiliki side effect yang terlihat dari luar. Write berbeda.

Jika dua attempt sama-sama dapat membuat charge, menambahkan event, mengirim message, atau mengubah state, cancellation tidak cukup. Losing request mungkin sudah commit sebelum caller menerima hasil pemenang.

Write hedge membutuhkan mekanisme idempotency pada level operasi atau protokol deduplication lain dengan semantics yang mencakup attempt concurrent. Idempotency key yang stabil dapat membuat sistem penerima mengenali kedua attempt sebagai satu operasi logis, tetapi hanya jika key tersebut ditegakkan pada boundary tempat side effect menjadi durable.

Meski demikian, hedging pada write memerlukan alasan khusus. Eksekusi duplikat dapat meningkatkan lock contention dan membuat observability lebih rumit. Manfaat latency perlu ditimbang terhadap constraint correctness dan capacity pada mutation path.

Capacity limit harus berada di samping kebijakan hedge

Hedging memakai kapasitas cadangan. Dalam kondisi normal, cadangan itu mungkin murah. Saat overload, kerja duplikat dapat membentuk positive feedback loop:

service slows
    -> more requests cross hedge delay
    -> more duplicate work arrives
    -> queues grow
    -> service slows further

Implementasi yang aman membatasi traffic tambahan secara terpisah dari traffic biasa. Kontrol yang umum mencakup hedge budget, concurrency limit untuk hedged attempt, dan penghentian hedge ketika downstream melaporkan saturation.

Sebagai contoh, caller dapat mengizinkan hedge hanya ketika kedua kondisi berikut terpenuhi:

hedges_in_flight < hedge_concurrency_limit
hedges_issued / original_requests < hedge_rate_budget

Budget sebaiknya diukur dalam window yang jelas, bukan diperlakukan sebagai persentase abstrak. Burst hedge tetap dapat berdampak meski rata-rata jangka panjang terlihat kecil.

Admission control juga perlu menentukan prioritas kerja. Dalam banyak sistem, original request sebaiknya memperoleh akses ke kapasitas sebelum speculative duplicate. Pool terpisah atau scheduling priority yang lebih rendah untuk hedge dapat mempertahankan perbedaan tersebut.

Delay harus mengikuti operasi, bukan seluruh service

Satu hedge delay global jarang tepat. Endpoint dan request class yang berbeda dapat memiliki distribusi service time yang sangat berbeda.

Delay 40 ms mungkin terlambat untuk cache lookup tetapi agresif untuk pencarian kompleks. Mencampurkan keduanya ke dalam satu persentil menghasilkan threshold yang tidak mewakili keduanya dengan baik.

Delay dapat diturunkan dari latency terbaru untuk request class yang stabil, disertai batas terhadap perubahan mendadak dan sampel yang terlalu sedikit. Delay juga dapat dikonfigurasi dari service-level budget yang eksplisit. Dalam kedua pendekatan, metric harus mewakili tahap yang sama dengan tahap yang dikendalikan kebijakan hedge.

Queue time memerlukan perhatian khusus. Jika client mengukur end-to-end latency tetapi metric server baru dimulai setelah worker memproses request, threshold yang dipilih dapat melewatkan queueing yang mendominasi keterlambatan yang terlihat pengguna.

Karena itu, hedge delay merupakan parameter operasional, bukan konstanta universal. Nilainya perlu observable, bounded, dan diubah dengan kehati-hatian yang sama seperti load-control setting lain.

Ukur penurunan latency bersama work multiplier

Rollout yang sehat perlu mengukur kedua sisi pertukaran.

Latency metric dapat membandingkan p50, p95, p99, dan deadline-exceeded rate sebelum dan sesudah hedging. Capacity metric perlu melacak tambahan request rate, CPU time, database query, byte yang ditransfer, connection occupancy, dan efektivitas cancellation yang berasal dari hedge.

Rasio yang berguna adalah:

work multiplier =
    total physical attempts
    / logical requests

Nilai yang mendekati 1 berarti hedging jarang terjadi. Multiplier yang naik berarti lebih banyak logical request memakai beberapa eksekusi. Nilai yang dapat diterima bergantung pada kapasitas cadangan dan biaya tiap attempt; tidak ada target universal yang selalu aman.

Pengujian perlu mencakup overload, bukan hanya steady state yang sehat. Kebijakan yang memperbaiki p99 pada load ringan dapat membuat dependency yang saturated menjadi tidak stabil jika hedge rate ikut naik bersama latency.

Hedged request paling efektif ketika slow attempt hanya sesekali terjadi, execution path cukup independen, operasi aman untuk diduplikasi, cancellation benar-benar bermakna, dan kapasitas cadangan memiliki budget yang eksplisit. Dalam kondisi tersebut, speculative work dapat menukar sedikit eksekusi redundan dengan tail yang lebih sempit tanpa mengubah setiap request menjadi duplikasi permanen.