Propagasi Deadline Menghentikan Pekerjaan Setelah Caller Menyerah

Timeout di edge tidak otomatis menghentikan pekerjaan yang berjalan lebih dalam di sistem. Client dapat meninggalkan request setelah dua detik sementara API server masih menunggu service lain, dan service tersebut mungkin masih menjalankan query database. Response sudah kehilangan penerimanya, tetapi CPU time, connection, memory, posisi queue, dan kapasitas downstream dapat tetap terpakai.

Propagasi deadline membawa budget waktu milik caller melewati batas-batas tersebut. Setiap komponen menerima deadline absolut atau remaining budget yang setara, menolak pekerjaan yang tidak sempat dimulai, dan membatalkan operasi saat budget habis. Tujuannya bukan sekadar membuat kegagalan terjadi lebih cepat. Mekanisme ini mencegah pekerjaan tanpa guna hidup lebih lama daripada request yang menjadi alasan pekerjaan itu ada.

Timeout independen dapat melewati budget caller

Misalkan public API memiliki deadline request 900 ms. API tersebut memanggil inventory service lalu pricing service. Jika setiap downstream client memiliki timeout tetap 800 ms, request dapat menghabiskan hampir 800 ms di inventory lalu memulai panggilan pricing dengan jatah 800 ms baru. Masing-masing konfigurasi memang memiliki batas, tetapi urutannya tidak mematuhi budget awal 900 ms.

Deadline yang diteruskan mengubah panggilan kedua. Jika inventory menghabiskan 650 ms, pricing menerima sekitar 250 ms dikurangi reserve untuk pemrosesan lokal dan pengiriman response. Operasi downstream tidak memperoleh timeout penuh yang baru hanya karena melewati batas process.

Prinsip yang sama berlaku pada fan-out. Panggilan paralel dapat berbagi deadline absolut yang sama meski durasi yang diharapkan berbeda. Child operation boleh memakai waktu lebih sedikit dari budget yang tersisa, tetapi tidak semestinya memperpanjang parent request melewati masa gunanya.

Deadline absolut tidak mereset waktu di setiap hop

Meneruskan durasi seperti 500ms saja dapat mereset clock tanpa sengaja pada setiap hop. Service A memulai timer 500 ms, menunggu 150 ms, lalu memberi tahu service B bahwa tersedia 500 ms. Service B kini dapat terus berjalan 150 ms melewati budget yang awalnya diterima service A.

Deadline absolut menghindari reset tersebut:

request_deadline = 14:03:27.450

service_a_now = 14:03:27.100
remaining = request_deadline - service_a_now

if remaining <= 0:
    reject_as_expired()
else:
    call_service_b(deadline=request_deadline)

Di dalam satu process, banyak runtime menyediakan cancellation context atau token yang menggabungkan deadline dengan cancellation signal eksplisit. Di seberang network boundary, protocol memerlukan representasi yang mempertahankan budget tersebut. Penerima tetap perlu menerapkan safety limit miliknya sendiri, bukan menerima nilai caller tanpa batas.

Clock skew membuat timestamp wall clock absolut antar-host lebih rumit. Protocol dapat menanganinya melalui timeout header dengan definisi yang ketat, monotonic clock di dalam tiap process, konversi terbatas pada hop boundary, atau infrastructure yang memperhitungkan transit time. Invariannya tetap sama: meneruskan request tidak boleh memberinya waktu berguna lebih banyak daripada yang dimiliki parent.

Cancellation harus mencapai operasi yang memegang resource

Menandai HTTP handler sebagai cancelled belum cukup jika database driver tetap menjalankan query. Cancellation signal harus mencapai layer yang dapat melepas resource langka.

Satu rantai umum dapat mencakup inbound request context, RPC client, downstream handler, connection pool, dan database command. Setiap layer memerlukan cancellation semantics yang menghentikan operasi atau menyatakan dengan jelas bahwa operasi tersebut tidak dapat diinterupsi. Jika driver tidak dapat membatalkan operasi in-flight, aplikasi dapat berhenti menunggu sementara database connection tetap terpakai sampai command selesai.

Keterbatasan itu memengaruhi capacity planning. Timeout dapat membatasi latency yang terlihat caller tanpa membatasi lama resource ditempati. Karena itu, metric sebaiknya membedakan request cancellation dari downstream termination yang benar-benar terkonfirmasi jika perbedaannya dapat diamati.

Cancellation juga memerlukan cleanup. Lock, permit, temporary file, transaction, dan connection lease yang diperoleh sebelum cancellation tetap harus dilepas secara deterministik. Jalur kode cancelled yang melewati cleanup mengubah kontrol latency menjadi resource leak.

Child budget dapat lebih pendek dari parent budget

Propagasi menetapkan batas atas, bukan kewajiban untuk menghabiskan setiap milidetik yang tersisa. Service sering masih membutuhkan waktu setelah dependency selesai untuk memvalidasi data, melakukan commit state lokal, mengenkode response, atau menjalankan compensating logic.

Jika tersisa 300 ms dan penyelesaian lokal biasanya membutuhkan hingga 60 ms, downstream call dapat diberi paling banyak 240 ms. Reserve tersebut sebaiknya berasal dari pekerjaan nyata dan latency yang teramati, bukan cushion besar yang dipilih tanpa dasar.

Dependency yang berbeda juga dapat memiliki local cap yang lebih ketat. Service dengan sisa 700 ms dapat membatasi cache lookup menjadi 20 ms karena menunggu lebih lama akan membuat fallback lebih masuk akal. Effective child deadline adalah nilai yang lebih awal antara propagated parent deadline dan local cap milik komponen.

child_deadline = min(parent_deadline, now + local_cap)

Aturan ini mempertahankan boundary milik parent sambil memberi setiap komponen ruang untuk menerapkan service policy yang lebih ketat.

Waktu di queue memakai budget yang sama

Umur request terus bertambah saat menunggu worker, semaphore, connection, atau rate-limit token. Jika pengecekan deadline baru dimulai setelah admission, request dapat duduk di queue sampai masa gunanya habis lalu tetap mengambil kapasitas eksekusi.

Sistem dengan batas kapasitas perlu menghitung queue residence sebagai bagian dari end-to-end budget. Request yang sudah expired dapat dikeluarkan atau ditolak sebelum memperoleh scarce resource berikutnya. Jika cancellation-aware queue dapat diterapkan, entry dapat dibuang saat context-nya expired tanpa menunggu entry mencapai kepala queue.

Perilaku ini sangat penting saat overload. Queue panjang membentuk reservoir pekerjaan basi. Menjalankan reservoir itu setelah caller pergi akan menunda request yang lebih baru dan dapat mempertahankan saturasi service bahkan setelah incoming traffic turun.

Retry memakai deadline awal yang sama

Retry adalah attempt lain dalam operasi yang sama, bukan hak atas budget waktu baru. Backoff, connection setup, DNS resolution, TLS negotiation, dan attempt sebelumnya semuanya memakan waktu.

Sebelum retry, caller perlu membandingkan remaining budget dengan biaya attempt lain yang masih masuk akal. Memulai attempt dengan sisa beberapa milidetik dapat menambah load downstream tanpa jalur realistis menuju response yang masih berguna.

Retry library juga memerlukan propagated cancellation signal. Backoff sleep harus berakhir saat parent deadline expired. Tanpa itu, request dapat tetap hidup hanya karena retry scheduler belum selesai menunggu.

Hedged request mengikuti disiplin yang sama. Jika salinan kedua diluncurkan untuk menekan tail latency, kedua salinan tetap menjadi child dari parent budget yang sama, dan salinan yang kalah perlu dibatalkan setelah satu hasil yang dapat diterima menang. Eksekusi duplikat yang terus berjalan setelah pemenang dipilih menghabiskan kapasitas tanpa memperbaiki response yang sudah selesai.

Background work memerlukan transfer ownership yang eksplisit

Tidak semua operasi harus mati bersama request. Handler dapat menerima command dan secara sengaja menyerahkan pekerjaan durable ke queue untuk diproses kemudian. Dalam kasus itu, background job memiliki lifecycle baru dan tidak semestinya mewarisi cancellation token yang terikat pada client connection.

Transfer ownership harus eksplisit. Menyimpan job, memublikasikan durable message, atau melakukan commit state dapat menetapkan bahwa sistem menerima tanggung jawab secara independen dari caller. Sekadar menjalankan goroutine, task, atau thread dari request handler tidak menciptakan durable ownership dan dapat membuat cancellation behavior menjadi ambigu.

Kesalahan sebaliknya juga sering terjadi: melepaskan ordinary downstream work dari request hanya agar tidak terkena cancellation. Langkah itu mempertahankan pekerjaan yang hasilnya tidak memiliki penerima dan menghilangkan perlindungan kapasitas yang diberikan propagasi.

Observability perlu memperlihatkan budget yang habis

Timeout metric menjadi lebih berguna jika menunjukkan lokasi budget berakhir. Signal yang relevan mencakup remaining budget saat service entry, queue wait time, downstream duration, cancellation count, pekerjaan yang ditolak karena sudah expired, dan operasi yang terus berjalan setelah upstream cancellation.

Tracing dapat membawa timeline request melewati beberapa hop. Trace dengan parent span yang berakhir pada deadline sementara child span terus berjalan beberapa detik menunjukkan cancellation yang hilang atau tidak efektif. Banyak request yang tiba dengan remaining budget hampir nol dapat menandakan upstream queueing berlebihan, biaya retry yang tinggi, atau timeout policy yang terlalu ketat untuk route tersebut.

Log sebaiknya tidak memperlakukan deadline expiry yang memang diperkirakan sebagai internal error generik. Caller cancellation, local dependency cap, dan downstream failure memiliki arti operasional berbeda meski ketiganya sama-sama membuat response tidak selesai.

Deadline membentuk capacity boundary sekaligus latency boundary

Dampak propagasi paling kuat terlihat saat partial failure dan overload. Tanpanya, request yang sudah timeout dapat terus memakai resource di balik titik kegagalan yang terlihat, sehingga menambah tekanan pada sistem yang memang sedang lambat. Pekerjaan baru kemudian bersaing dengan request yang caller-nya sudah pergi.

Propagated deadline mengikat konsumsi resource pada masa hidup demand yang masih berguna. Mekanisme ini tidak menjamin setiap dependency dapat langsung dibatalkan dan tidak menggantikan concurrency limit, queue bound, circuit breaker, atau load shedding. Deadline memberi semua kontrol tersebut temporal boundary yang sama: setelah budget request habis, pekerjaan baru untuk request itu harus berhenti dan pekerjaan yang dapat dibatalkan perlu melepaskan klaimnya atas kapasitas.