Propagasi Deadline Menjaga Budget Timeout Antar-RPC Hop

Timeout yang dimulai ulang pada setiap boundary service dapat mengubah budget caller yang singkat menjadi rangkaian work yang jauh lebih lama. Client mungkin memberi waktu 800 milidetik, service A menghabiskan 500 milidetik untuk proses lokal, lalu memanggil service B dengan timeout baru sebesar 800 milidetik. B dapat terus bekerja lama setelah client berhenti menunggu.

Propagasi deadline mempertahankan satu waktu akhir pada request. Setiap hop menghitung sisa budget dari deadline tersebut dan tidak memulai work yang tidak lagi muat di dalamnya. Mekanisme ini tidak otomatis membuat eksekusi lebih cepat. Hasil utamanya adalah eksekusi terbatas yang mematuhi kontrak waktu dari upstream.

client mulai pada t=0
absolute deadline = t=800ms

A menerima pada t=80ms   -> tersisa 720ms
B menerima pada t=430ms  -> tersisa 370ms
DB call pada t=610ms     -> tersisa 190ms

Budget terus berkurang ketika queueing, network transit, retry, dan komputasi lokal memakai waktu.

Deadline lebih kuat daripada duration baru

Relative timeout seperti 500ms menjelaskan durasi sejak sebuah komponen memulai timer. Meneruskan duration yang sama ke downstream membuat budget baru, bukan mempertahankan budget awal.

Absolute deadline menyatakan titik waktu setelah caller tidak lagi menginginkan operasi terus berjalan. Sebuah proses dapat mengubahnya menjadi remaining duration lokal tepat sebelum memulai child operation:

remaining = deadline - now
if remaining <= 0:
    fail before starting downstream work

RPC framework sering menyediakan metadata deadline atau cancellation secara langsung. Meski demikian, application code tetap harus mempertahankannya ketika membuat child context, background task, queue message, atau custom protocol call. Library tidak dapat meneruskan informasi melewati application boundary yang membuangnya.

Setiap hop memerlukan usable budget yang lebih kecil

Meneruskan upstream deadline tanpa perubahan bukan berarti setiap downstream call boleh memakai seluruh waktu yang tersisa. Service biasanya memerlukan cadangan untuk response serialization, network return, cleanup, atau alternate path.

Karena itu, child deadline dapat dibatasi sebelum dispatch:

child_deadline = min(parent_deadline, now + local_cap)

Service dengan sisa 300 milidetik mungkin memberi dependency paling banyak 220 milidetik dan menyimpan sisanya untuk penyelesaian lokal. Besar cadangan bergantung pada workload. Persentase tetap dapat berperilaku buruk ketika budget sangat bervariasi, sedangkan margin tetap dapat mendominasi request yang sangat singkat.

Invariant-nya lebih sederhana daripada tuning tersebut: child operation tidak boleh mendapat izin berjalan melewati deadline parent request.

Queue time memakai budget yang sama

Work yang menunggu di thread pool, executor, connection pool, atau admission queue sudah memakai waktu caller. Memulai dependency timeout penuh hanya setelah request keluar dari queue menyembunyikan biaya tersebut.

Deadline-aware admission dapat menolak work ketika sisa budget sudah terlalu kecil:

if remaining < minimum_useful_budget:
    reject_or_cancel()
else:
    enqueue_with_deadline()

Queue juga sebaiknya tidak mengeksekusi item yang sudah expired hanya karena item itu akhirnya mencapai posisi depan. Menghapus atau melewati work yang expired mengurangi pemakaian resource saat overload, ketika queue delay biasanya paling besar.

Mekanisme ini tidak menggantikan bounded queue atau concurrency limit. Deadline membatasi kegunaan berdasarkan waktu; admission control membatasi jumlah work yang masuk ke sistem.

Retry memakai satu budget

Retry mudah melipatgandakan latency ketika setiap attempt memperoleh timeout baru. Tiga attempt dengan timeout 400 milidetik per attempt dapat menghabiskan lebih dari budget caller satu detik setelah backoff dan network delay ikut dihitung.

Retry loop sebaiknya menghitung ulang remaining time sebelum setiap attempt dan backoff:

while retryable:
    remaining = deadline - now
    if remaining <= minimum_attempt_budget:
        stop

    attempt_timeout = min(per_attempt_cap, remaining - reserve)
    run_attempt(attempt_timeout)

Backoff juga merupakan bagian dari budget. Sleep melewati titik ketika attempt berikutnya masih mungkin selesai dengan berguna hanya menunda penyelesaian request yang sudah pasti melewati deadline.

Retry policy tetap dapat berhenti lebih awal karena attempt limit, non-retryable error, circuit state, atau retry budget. Propagasi deadline memberi batas temporal terluar, bukan keseluruhan retry policy.

Cancellation dan deadline berkaitan tetapi berbeda

Deadline adalah cancellation yang dapat diprediksi berdasarkan waktu. Explicit cancellation dapat terjadi lebih awal karena user disconnect, parent task gagal, hedged request sudah menghasilkan result, atau caller meninggalkan operasi karena alasan lain.

Kedua signal biasanya perlu mengalir melalui request tree yang sama. Child yang hanya memantau clock dapat terus berjalan setelah explicit cancellation. Child yang hanya memantau cancellation flag dapat berjalan tanpa batas jika tidak ada pihak yang memicunya.

Resource cleanup juga penting. Cancellation perlu melepaskan connection, lock, buffer, goroutine, task, dan request-scoped state lain sesuai aturan runtime. Meneruskan signal tanpa membuat blocking operation responsif terhadapnya masih menyisakan banyak work yang sia-sia.

Representasi clock penting pada process boundary

Di dalam satu process, monotonic clock ideal untuk mengukur elapsed time karena penyesuaian wall clock tidak mengubah duration. Di antara beberapa machine, raw monotonic timestamp umumnya tidak dapat dibandingkan karena setiap host memiliki clock origin sendiri.

Protocol karena itu memerlukan representasi dengan cross-host semantics yang jelas. Sebagian sistem mengirim absolute wall-clock deadline lalu membangun kembali local timer. Sistem lain mengirim remaining duration dan memperhitungkan transport sesuai semantics protokol. Semantics milik framework sebaiknya diikuti daripada membuat format timestamp sendiri tanpa kontrak yang jelas.

Clock skew dapat memengaruhi absolute deadline yang dipertukarkan antar-host. Sistem dengan timing requirement ketat perlu memperhitungkan ketidakpastian tersebut, biasanya melalui synchronized clock, conservative margin, atau protocol semantics yang mengurangi ketergantungan pada remote wall time.

Observability perlu menunjukkan konsumsi budget

Satu counter timeout tidak menunjukkan lokasi budget habis. Telemetry yang berguna mencatat original budget, remaining budget pada hop utama, queue delay, dependency duration, retry count, dan komponen yang mendeteksi expiry.

request_budget_ms
remaining_budget_ms
queue_delay_ms
dependency_duration_ms
retry_attempts_total
deadline_exceeded_total
cancelled_total

Tracing sangat berguna karena budget yang terus mengecil dapat dilihat melintasi service boundary. Span yang dimulai dengan sisa hanya 40 milidetik memberi konteks berbeda dari dependency yang menghabiskan 700 milidetik setelah menerima hampir seluruh budget.

Propagasi deadline memberi satu temporal boundary pada request, bukan rangkaian timer yang tidak berkaitan. Queueing, local work, dependency call, dan retry semuanya memakai boundary yang sama. Ketika budget habis, downstream work tidak lagi mendapat izin untuk terus berjalan, sehingga pemakaian resource tetap selaras dengan request yang masih dapat menghasilkan result berguna.