Overview
A timeout limits how long a caller waits for an operation before giving up. Without timeouts, a slow or hung dependency makes callers wait indefinitely, holding threads, connections, and memory until the caller itself fails. Timeouts are the most basic and most important resilience mechanism.
There are several kinds: connection timeouts (establishing a connection), request or read timeouts (waiting for a response), and overall deadlines that cover the entire operation including retries. Good timeouts are based on measured latency (for example slightly above the dependency's P99) and deadlines should propagate downstream so services do not keep working on requests whose callers have already given up.
You agree to wait 15 minutes. If they have not shown up by then, you leave instead of waiting all day, and you text them so they do not show up to an empty table (deadline propagation).
When to use it
- Every network call: HTTP, gRPC, database, cache, message broker.
- Long-running operations that could hang.
- User-facing requests with latency budgets.
- Locks and leases.
Where it shows up in interviews
Recognize it when: the whole request must finish in 500 ms.
- Design a search API with strict SLOs
- Design a checkout with multiple dependencies
Where it is used in real software
Clients set deadlines that propagate to downstream calls; servers can check remaining time and stop early.
Many HTTP clients historically had no default timeout, a frequent cause of production outages.
Service meshes enforce per-route and per-try timeouts centrally.
Key terms
- Connect timeout
- Max time to establish a connection.
- Read / request timeout
- Max time to receive a response.
- Deadline
- Absolute time by which the whole operation must finish.
- Deadline propagation
- Passing the remaining time to downstream calls.
- Cancellation
- Stopping work once the result is no longer needed.
How it works, step by step
- 1Measure dependency latency
P99 and P99.9.
- 2Set timeouts slightly above P99
Plus margin; shorter connect timeouts.
- 3Set an overall deadline
From the user-facing budget.
- 4Propagate remaining time
Headers or gRPC deadlines.
- 5Cancel on timeout
Abort in-flight work and free resources.
Timeout budget for a 1-second API
Checkout endpoint with 3 dependencies
| Step | Timeout | Remaining budget |
|---|---|---|
| Request received | - | 1,000 ms |
| Cart service (P99 80 ms) | 150 ms | 850 ms |
| Pricing + inventory in parallel (P99 120 ms) | 200 ms | 650 ms |
| Payment (P99 400 ms) | 550 ms | 100 ms |
| Write order and respond | 80 ms | 20 ms margin |
NOWStep: Request received | Timeout: - | Remaining budget: 1,000 ms
Downstream timeouts must fit within the caller's budget; a 5-second payment timeout inside a 1-second API is meaningless.
Implementation
// Deadline-aware fetch using AbortSignalexport async function callWithDeadline(url: string, deadline: number, init: RequestInit = {}) { const remaining = deadline - Date.now(); if (remaining <= 0) throw new Error("Deadline exceeded before call"); const res = await fetch(url, { ...init, signal: AbortSignal.timeout(Math.min(remaining, 2_000)), // never wait longer than the remaining budget headers: { ...init.headers, "x-request-deadline": String(deadline) }, // propagate downstream }); if (!res.ok) throw Object.assign(new Error(`HTTP ${res.status}`), { status: res.status }); return res.json();} // In a handler: derive the deadline from the incoming request or a default budgetapp.get("/checkout", async (req, res) => { const deadline = Number(req.header("x-request-deadline")) || Date.now() + 1_000; const cart = await callWithDeadline(`${CART_URL}/carts/${req.query.id}`, deadline); res.json(cart);});Complexity and performance
Per dependency.
Prevents pile-ups.
Trade-offs
Short timeouts fail fast and protect resources but may cut off legitimate slow requests; long ones tolerate slowness but risk resource exhaustion.
Timeout + retry can exceed the caller's budget; use deadlines to bound the total.
Variants and related techniques
Adjust based on observed latency.
Close connections with no activity.
Common mistakes
- Relying on library defaults.
Fix: Many are infinite or very long; set them explicitly.
- Downstream timeouts longer than upstream.
Fix: Callers give up while downstream keeps working; propagate deadlines.
- Timing out without cancelling work.
Fix: Abort queries and requests to free resources.
Interview questions
How do you choose a timeout value?
Base it on the dependency's measured latency distribution, slightly above its P99, and make sure it fits within the caller's overall latency budget including any retries.
What is deadline propagation?
Passing the absolute deadline or remaining time with each downstream call, so every service knows how long the original caller will wait and can stop work that can no longer be useful.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Create a timeout budget for a 3-hop request | Easy | Budgeting. |
| Implement deadline propagation across services | Medium | Headers and cancellation. |