Performance

Find why .NET requests time out before making customers wait longer

Follow one failing operation through the application and its dependencies. Use the evidence to decide which change deserves engineering time, and whether a longer wait would help at all.

Published · AI disclosure

When a customer cannot retrieve an order's status, a longer timeout may leave them waiting longer for the same failure. Meanwhile, the team still needs to decide whether to change application code, investigate a dependency, or add capacity.

Take an order-status endpoint as an illustrative example. Keep the investigation focused on three needs:

  • A usable response within the agreed waiting time
  • Evidence of where the request spends that time
  • A change that still works when requests arrive together

Find the deadline that expired

Start with one failed request. Record its timestamp, trace ID, deployed version, duration and error. Identify which component stopped waiting: the gateway, an application timeout policy, the outbound HTTP client or the database driver.

Those boundaries can enforce different limits. Changing one may leave another unchanged. Agree the acceptable customer wait first, then map the configured deadlines along this request's path.

For example, ASP.NET Core's request-timeout middleware must be configured explicitly. When its deadline expires, it signals cancellation through HttpContext.RequestAborted; it does not automatically abort the request. A timeout response alone therefore does not establish that the underlying work stopped. ASP.NET Core timeout behavior

The result should be specific: which deadline expired, during which operation, and what happened afterward. That determines where to collect the next evidence.

Follow the slow request across its dependencies

The next question is where the waiting happened.

Use a correlated trace to compare the incoming request with its database and outbound HTTP operations. In .NET, an Activity represents a timed operation; parent and child activities connect the steps. The application also needs configured instrumentation and a collector to retain useful traces. Missing spans are missing evidence, rather than proof that a dependency was fast. Distributed tracing in .NET

Inspect the timeline. Do not simply add child durations, because parallel operations can overlap. Compare a failed request with a successful one running the same operation.

A long outbound HTTP operation narrows the search, but does it mean the remote service is slow? On .NET 8 and later, http.client.request.time_in_queue measures time waiting for an available connection before sending. Check that alongside request duration and the downstream service's own evidence. The built-in HTTP duration metric ends after response headers; reading a large body can take additional time. HTTP client metric boundaries

If the database operation dominates, investigate that command and its waits. If the time remains unexplained inside the application, move to runtime evidence.

Check whether .NET is working or waiting

Low CPU does not mean the application has spare capacity to serve requests. Microsoft's reproducible ThreadPool-starvation example shows how blocking calls can leave queued requests waiting for workers. Rising worker counts with comparatively low CPU are clues; thread stacks help establish what is blocking them. Microsoft's starvation investigation

First use existing monitoring. For an authorized ad-hoc diagnostic session, these commands illustrate a runtime-counter view and a stack snapshot:

Diagnostic commands
dotnet-counters monitor --process-id 12345 --counters System.Runtime
dotnet-stack report --process-id 12345

Replace 12345 with the target process ID. The tools must be installed, compatible with the runtime and permitted to attach. Inspect counters during the slow period, then take a stack snapshot if symptoms persist. A snapshot can miss an intermittent stall; a time-bounded trace is more useful then. Choose an approved capture window and protect the output. Counter command reference, stack command reference

If stacks repeatedly show .Result or .Wait() blocking on asynchronous I/O, follow that path and use await through its callers. High CPU with runnable work instead calls for CPU profiling. The evidence should choose the change.

Carry the timeout decision into the work

Once the cause is located, change that part of the path. Avoid changing concurrency, capacity and every timeout together; doing so makes the result harder to explain.

Cancellation needs attention too. Inside an ASP.NET Core handler, an existing, correctly configured HttpClient can receive the request's cancellation token:

C#
using var response = await httpClient.GetAsync(
    "inventory/availability",
    context.RequestAborted);

This fragment assumes the client's BaseAddress is configured and context is the current HttpContext. It allows this HTTP operation to observe cancellation. It does not undo work already accepted by the remote service. Keep retries within the agreed request budget, and establish whether repeating an operation is safe before adding them. Cancellation overload, HTTP resilience guidance

Decide whether the customer problem is resolved

Repeat the same operation under comparable data and concurrent demand. Compare:

  • Correct results and completed requests
  • Tail latency, such as the 95th percentile, and timeout frequency
  • The wait, queue or blocking signal that justified the change
  • Resource use and whether canceled work actually stops

Close the investigation as resolved when the agreed operation meets its response and correctness criteria under the tested demand. If the cause remains unproven, deliver the observed request path, evidence gaps, ranked hypotheses and the next measurement needed to distinguish them. Any longer timeout should remain an explicit customer-experience tradeoff supported by measurements.

Example scope: commands and APIs were checked against Microsoft documentation. These fragments were not executed against an application, and no production capture or performance benchmark was run.

Discuss a performance investigation

Sources

Written with AI and not reviewed by a human. It may contain mistakes.