Timeouts don't compose the way you would hope
Three services each with a reasonable-looking timeout can add up to a client-facing timeout nobody configured on purpose. Budget the whole chain, not each hop.
When setting up a new microservice, the standard practice is to configure strict network timeouts for every external call. Five seconds for a database query, three seconds for an internal API, and perhaps ten seconds for a third party integration. In isolation, these numbers look perfectly logical. But in a deep call graph, localized timeouts combine to create silent, compounding failures.
The Accumulation Problem Imagine a typical synchronous request path. An API gateway calls an orchestration service, which calls a validation service, which finally queries a database.
If the orchestration service has an internal timeout of ten seconds and the validation service has a timeout of eight seconds, the engineering team assumes the system is safely bounded. However, if the database takes seven seconds to respond under heavy load, the validation service succeeds just under the wire. By the time the orchestration service receives the payload and resumes its own processing, it has already burned through its ten second budget.
The orchestration service abruptly drops the connection. The database query was successful, the compute cycles were spent, and the downstream services functioned exactly as configured. Yet, the client still receives an HTTP 504 Gateway Timeout.
Processing Dead Requests This scenario creates a massive amount of wasted infrastructure spend and unnecessary load. Deep dependencies are doing expensive computational work for a parent request that has already been abandoned by the client. The system is effectively processing dead requests, consuming thread pools and database connections for transactions that will never reach the user.
The Global Latency Budget The fix requires shifting from localized timeouts to global latency budgets. You cannot configure timeouts in a vacuum. You have to budget the entire request chain from the API gateway down to the lowest storage tier.
To implement this programmatically, you must pass the remaining time allowance down through the network stack. In gRPC, this is handled natively via deadline propagation. In standard REST architectures, it requires injecting custom HTTP headers that track when the original request was initiated and the absolute maximum time it is allowed to live.
Enforcing the Deadline Before any service in the chain begins a computationally expensive task or initiates another outbound network call, it must explicitly check the remaining global budget. If a worker realizes it only has fifty milliseconds left to complete a database operation that historically takes two seconds, it should not even attempt the query. It must immediately abort the operation and propagate the timeout error back up the chain.
Failing fast preserves compute capacity. Building a resilient distributed system means recognizing that time is a finite, shared resource that must be tracked continuously across every single network boundary.