The delivery-rate service is slow, and checkout keeps waiting. The server is running and reports few errors. Customers see a spinner while new requests occupy connections and memory before earlier ones have finished. Everything is technically alive; very little is usable.
A timeout limits waiting. The product also needs a limit for the complete journey, an outcome when it expires and a policy for work whose caller has left.
The customer waits for the whole screen
Delivery options may require address validation, rates and available slots. If each sequential call receives a fresh full timeout, total waiting time grows. Meeting every team’s local target does not make the complete journey acceptable.
A deadline is the latest acceptable completion time for a particular operation. Passing the remaining time downstream prevents each service from starting the clock again. A dependency can still have a shorter local timeout when it is optional or when the caller needs time to process the response.
For illustration, suppose the product allows two seconds for a response and earlier steps have already used half a second. The next service cannot reasonably begin another two-second wait. These are illustrative values. Actual limits require workload measurements.
Parallel calls share the remaining elapsed time rather than necessarily dividing it equally. Leave room to assemble and return the response before the customer-facing deadline.
Find out what the timer actually covers
A timeout may cover connection setup, waiting for the next data block or the whole request. DNS, secure connection setup and queueing for a free connection may sit outside that timer.
Trace where measurement starts. A newly started application establishing its first connection can behave differently from one reusing warm connections. Test both.
Average latency hides the long tail. Examine distributions, expected peaks and acceptable false timeouts. Short limits create false failures and extra retries; long ones let waiting consume capacity for longer.
The caller stopped waiting. The server may still be busy
Closing a client connection is not automatically an instruction to stop a database calculation. A handler may continue calling other services, holding connections or building a report. Under load, this abandoned work competes with requests from people who are still waiting.
Cancellation has to reach components capable of acting on it. Application code must observe the signal, stop unnecessary work and release resources. The gRPC cancellation guide explicitly distinguishes signalling cancellation from forcibly interrupting application code: the handler has a part to play.
Cancellation is also not rollback. If a change has already committed, or a partner has accepted a booking, closing the request does not restore the earlier state. A timeout says that a response did not arrive in time. It does not prove that the business operation never happened.
A shipment booking therefore needs an operation identifier and a way to retrieve its status. Creating a fresh booking merely because the response was lost risks booking the same shipment twice. Timeouts belong alongside idempotency controls; they do not replace them.
Some work should outlive the screen
If the customer requested a large report and the system durably accepted the job, closing a browser tab need not cancel it. That job has its own lifecycle: queueing, execution state, an expiry policy and a way to collect the result later. “Accepted for processing” is a different promise from “finished successfully”.
Trouble starts when an ordinary request quietly turns into background work without a durable record of its state. The customer cannot tell whether to wait or start again, and support cannot explain what is happening. Background work needs explicit, durable acceptance and visible status.
A partial response requires a similar product decision. Delivery options without an estimated arrival time may be useful. Confirming a shipment without confirmation from the carrier is another matter. An exception handler cannot decide which missing facts the business is allowed to ignore.
Test the waiting, not just the error message
Slow down one dependency and inspect more than the browser result. Does the entire request stay inside its allowance? Are occupied resources released? Does downstream computation continue after cancellation? Does the client start another attempt after the overall deadline has already passed?
Then simulate losing the response after a successful write. The interface should distinguish a confirmed rejection from an unknown result and offer a way to resolve the uncertainty. That matters more than identical timeout settings everywhere.
Monitoring should distinguish queueing before execution, time spent in the dependency and work continuing after the caller leaves. That turns an increase in timeouts into a specific diagnostic question. Extending the timeout can be reasonable, but without this evidence it may simply make the same failure take longer.