Sebuah request dapat kehilangan kegunaannya sebelum semua proses yang menanganinya berhenti bekerja. HTTP client mungkin berhenti menunggu setelah dua detik, sementara service upstream masih menjalankan database query, RPC, dan rangkaian retry selama beberapa detik berikutnya. Operasi tersebut tetap memakai connection, CPU time, kapasitas queue, dan konkurensi downstream meski hasilnya sudah tidak memiliki penerima.

Deadline membuat batas kegunaan itu eksplisit. Propagasi deadline melalui call bertingkat memberi komponen yang terlibat satu batas atas bersama yang berasal dari request awal. Ini berbeda dari pemberian timeout independen pada setiap hop: timeout lokal membatasi operasi individual, sedangkan deadline yang dipropagasikan membatasi umur seluruh graph operasi.

Timeout independen dapat memperpanjang umur total

Pertimbangkan request yang masuk ke service A dengan budget client dua detik. A memanggil B, lalu B memanggil C. Jika setiap service memulai timeout dua detik baru saat menerima request, chain tersebut dapat tetap aktif setelah caller awal berhenti menunggu.

client:  |--------- 2 s ---------X
A:       |--------- 2 s ---------X
B:             |--------- 2 s ---------X
C:                   |--------- 2 s ---------X

Diagram ini tidak menyatakan bahwa setiap implementasi selalu menunggu sampai timeout habis. Diagram tersebut menunjukkan persoalan batas: timeout relatif baru pada setiap hop bukan constraint yang sama dengan satu deadline end-to-end.

Sebaliknya, deadline absolut ikut dibawa bersama request. Jika A menerima deadline yang menunjuk 12:00:02 dan menghabiskan 700 ms sebelum memanggil B, B memiliki sisa sekitar 1,3 detik, dengan tetap memperhitungkan karakteristik clock dan transport. B tidak membuat jatah dua detik baru.

Remaining budget dihitung pada setiap boundary

Service dapat memperoleh remaining budget dari deadline yang dipropagasikan:

remaining = deadline - current_time

Jika nilainya sudah nol atau negatif, memulai pekerjaan downstream baru umumnya tidak lagi berguna bagi request tersebut. Jika masih positif, service dapat membatasi operasi berikutnya pada interval itu atau pada local cap yang lebih kecil.

Local cap tetap berguna. Misalnya request masuk masih memiliki 30 detik, tetapi metadata lookup tertentu secara desain dibatasi paling lama 500 ms. Batas efektif secara konseptual dapat dinyatakan sebagai:

effective_deadline = min(incoming_deadline, now + local_cap)

Incoming deadline mencegah child operation hidup lebih lama daripada parent. Local cap mencegah satu child menghabiskan porsi berlebihan dari parent budget yang lebih besar.

Cancellation dan deadline membawa informasi berbeda

Cancellation menyatakan bahwa pekerjaan perlu dihentikan. Deadline menyatakan kapan pekerjaan tidak lagi berguna meski tidak ada sinyal cancellation eksplisit.

Beberapa runtime menggabungkan kedua konsep tersebut dalam satu context object. Pada Go, misalnya, context.Context dapat membawa deadline dan mengekspos sinyal cancellation melalui Done(). Derived context dapat memperpendek deadline parent tetapi tidak memperpanjangnya melalui context.WithTimeout:

ctx, cancel := context.WithTimeout(parent, 250*time.Millisecond)
defer cancel()

rows, err := db.QueryContext(ctx, query)

Batas semantiknya bergantung pada API yang dipanggil untuk menghormati context tersebut. Mengirim ctx ke function yang mengabaikan cancellation tidak memaksa pekerjaannya terputus. Demikian pula, cancellation pada application code tidak berarti setiap remote system telah melakukan rollback terhadap operasi yang telanjur diterima.

Perbedaan ini penting untuk side effect. Caller dapat meninggalkan RPC setelah deadline tercapai sementara remote service masih dapat melakukan commit atas mutasi yang diminta. Propagasi deadline membatasi waktu tunggu dan pekerjaan yang kooperatif; mekanisme ini tidak menciptakan transactional cancellation lintas process boundary.

Waktu di queue memakai budget yang sama

Deadline perlu mencakup waktu menunggu eksekusi, bukan hanya waktu di dalam handler. Request yang menunggu 900 ms di queue sebelum masuk ke worker sudah memakai 900 ms dari end-to-end budget.

Mengabaikan queue time dapat menghasilkan pola stale work:

deadline -----------X
queue:    [900 ms]
worker:            [starts near X]

Worker yang memeriksa deadline sebelum memulai pekerjaan mahal dapat membuang request yang hasilnya sudah tidak mungkin tiba tepat waktu. Hal ini sangat relevan saat overload meningkatkan queue latency: tanpa pemeriksaan deadline, sistem dapat memakai kapasitas langka untuk memproses request yang sudah kedaluwarsa dan menambah delay bagi request yang masih aktif.

Infrastruktur queue tidak otomatis memberikan semantik tersebut. Deadline harus direpresentasikan dalam message metadata atau kontrak lain yang bertahan melewati handoff, dan consumer memerlukan kebijakan eksplisit untuk pekerjaan yang sudah kedaluwarsa.

Fan-out memerlukan kebijakan budget, bukan salinan timeout yang sama

Sebuah service dapat memanggil beberapa dependency secara konkuren. Memberikan seluruh remaining budget kepada setiap child sah sebagai batas umur, tetapi cara itu tidak menyisakan waktu khusus untuk agregasi, serialisasi, atau fallback setelah child selesai.

request budget
|----------------------------------|
| fan-out calls          | assemble|
|------------------------|---------|

Service dapat menyisihkan sebagian budget untuk penyelesaian lokal atau menetapkan child deadline yang lebih ketat sesuai peran setiap dependency. Kebijakan tersebut bersifat application-specific; tidak ada persentase universal yang menghasilkan perilaku latency yang benar.

Fan-out konkuren juga mengubah perilaku cancellation. Jika satu child wajib gagal dan hasil keseluruhan tidak lagi dapat berhasil, membatalkan pekerjaan sibling dapat melepas resource lebih awal. Jika partial result diperbolehkan, membatalkan semua sibling pada failure pertama justru menerapkan kontrak berbeda. Penanganan deadline mengikuti semantik response, bukan menggantikannya.

Retry memakai satu budget

Retry merupakan titik lain tempat timeout lokal baru dapat memperpanjang pekerjaan tanpa sengaja. Tiga attempt dengan timeout satu detik masing-masing dapat memakai hampir tiga detik ditambah backoff dan scheduling overhead. Jika caller hanya memiliki sisa 1,5 detik, kebijakan retry tersebut tidak muat dalam batas request.

Retry loop perlu mengevaluasi ulang remaining budget sebelum attempt berikutnya. Loop juga dapat mensyaratkan sisa waktu yang cukup agar attempt memiliki jendela penyelesaian yang masuk akal. Memulai operasi dengan sisa beberapa milidetik dapat menambah load tanpa memberi peluang penyelesaian yang berguna.

Mekanisme retry juga memerlukan aturan keamanan operasi. Deadline tidak membuat mutasi non-idempotent aman untuk diulang. Retry eligibility, idempotency, dan time budgeting merupakan constraint terpisah yang semuanya harus terpenuhi untuk attempt berulang.

Propagasi transport memerlukan kontrak eksplisit

Di dalam satu process, runtime context dapat membawa deadline melalui function call. Saat melewati process boundary, transport memerlukan representasi. RPC framework dapat menentukan deadline metadata; HTTP API khusus dapat memakai application header atau protocol field lain.

Timestamp absolut memiliki persoalan sinkronisasi clock. Durasi relatif menghindari pengiriman wall-clock instant, tetapi waktu selama message berada di transport dapat hilang dari perhitungan kecuali receiver memperhitungkan interval tersebut melalui semantik protokol. RPC stack yang matang menetapkan perilakunya sendiri, sehingga application code sebaiknya memakai kontrak framework alih-alih membuat encoding yang tidak kompatibel.

Trust boundary juga penting. Deadline dari client yang tidak tepercaya merupakan input, bukan kewajiban untuk menyediakan resource tanpa batas. Server dapat membatasinya sesuai service policy:

accepted_deadline = min(client_deadline, server_max_deadline)

Aturan yang sama mencegah deadline client yang sangat jauh menonaktifkan kontrol resource lokal.

Expiration tidak membuktikan remote termination

Saat parent mencapai deadline, beberapa state mungkin terjadi di downstream: child mungkin sudah berhenti, sedang berhenti secara kooperatif, tertahan pada API tanpa dukungan cancellation, atau sudah menyelesaikan side effect tetapi response-nya hilang.

Untuk pekerjaan read-only, membuang hasil yang terlambat sering cukup sederhana. Untuk mutasi, caller mungkin memerlukan operation identifier, mekanisme idempotency, atau status query untuk menyelesaikan outcome yang ambigu. Menganggap deadline error sebagai bukti bahwa mutasi tidak terjadi menciptakan correctness bug pada interface boundary.

Resource cleanup memiliki batasan serupa. Membatalkan context dapat memberi sinyal pada operasi kooperatif, tetapi socket, transaction, goroutine, thread, subprocess, atau remote job dilepas sesuai API dan perilaku runtime masing-masing. Cancellation path harus diteruskan ke setiap resource owner.

Observability perlu mencatat remaining budget

Timeout metric merekam gejala akhir tetapi dapat menyembunyikan lokasi habisnya budget. Trace dan structured log menjadi lebih berguna ketika mencatat incoming deadline, remaining budget pada boundary utama, queue delay, durasi child call, dan penyebab cancellation.

Field tersebut memisahkan beberapa bentuk failure: dependency yang menghabiskan sebagian besar budget sehat, request yang tiba dalam kondisi hampir kedaluwarsa, queue yang menghabiskan budget sebelum eksekusi, atau child operation yang terus berjalan setelah parent cancellation.

Deadline dengan demikian lebih dari sekadar timer setting. Ia merupakan kontrak umur request. Nilainya berasal dari konsistensi kontrak tersebut saat melewati queue, retry, fan-out, process boundary, dan resource owner sambil tetap mempertahankan local cap serta aturan correctness yang spesifik terhadap operasi.