The Orphaned Work Problem
Timeouts exist per hop, but nobody tells downstream services when the caller already gave up:
client timeout: 800ms
└─► gateway (own timeout 900ms)
└─► pricing svc (timeout 5000ms!) starts expensive calc
... finishes at 4000ms, writes result, publishes event
nobody is listening. 100% wasted compute — and the write
may have side effects! every hop needs to know:
"the caller abandons at T; don't bother after that"
The Deadline Header
The fix: carry a deadline timestamp through every call:
request headers:
X-Request-Deadline: 2026-08-24T10:00:00.800Z (absolute)
or
grpc-timeout: 700m (gRPC does this natively)
each service on receipt:
remaining = deadline - now()
own budget = min(remaining × share, own default)
pass deadline - consumed onward to children
chain behavior: everyone gives up TOGETHER at T,
no orphaned computation, no zombie writes
Deadline vs Timeout
| Timeout | Deadline | |
|---|---|---|
| Scope | Single call | Whole request journey |
| Value | Duration (“500ms”) | Absolute time |
| Propagates? | No | Yes, through all hops |
| Answers | ”How long do I wait here?" | "Is anyone still listening?” |
Implement deadlines as propagated absolute times; per-hop timeouts become min(local_default, remaining_deadline).
Cancellation: The Active Half
Knowing the deadline is half of it; acting is the other:
context cancellation (Go ctx, gRPC, React Query-style tokens):
caller aborts → cancellation signal propagates
→ in-flight queries cancelled at DB level
→ loops check token between iterations
→ partial work discarded BEFORE committing side effects
without propagation hooks, deadlines only stop NEW work;
running work must poll/subscribe to learn it's unwanted
Budget Allocation Patterns
reserve-for-response: keep ~10% of budget for your own reply
proportional-split: divide remaining among N children
critical-path-first: give the serial dependency most of the budget
anti-pattern: each hop sets child timeout = FULL original budget.
5 hops × 1s "budgets" → worst case 5s for an 800ms request.
budgets shrink as they flow; never reset mid-chain
Observability Payoff
log/metric per hop: deadline_remaining_ms
traces show where budgets die:
pricing used 600ms of its 250ms slice ← instant diagnosis
deadline telemetry turns "requests feel slow sometimes"
into a precise map of which stage overspends when
Interview Framing
Multi-service designs earn points with one sentence: “deadlines propagate via headers; each hop’s timeout is bounded by remaining budget.” The follow-up probes orphaned side effects — connect to idempotency keys and transactional outbox. Candidates treating timeouts as purely local config miss the distributed-systems question hiding underneath.
Premium Content
Unlock Deadlines and all premium lessons with a subscription.
From ₹199.99/year — See plans