Skip to main content

Timeout Policy (Resilience)

The resilience timeout policy cancels an operation if it takes longer than the configured duration. This is the resilience-level timeout, independent of the pipeline-level IHasTimeout.

Basic Usage

var policy = ResiliencePolicy.Create()
.Timeout(TimeSpan.FromSeconds(5))
.Build();

string result = await policy.ExecuteAsync<string>(async ct =>
{
// ct will be cancelled after 5 seconds
return await _httpClient.GetStringAsync(url, ct);
});

Configuration Options

var policy = ResiliencePolicy.Create()
.Timeout(opts =>
{
opts.Timeout = TimeSpan.FromSeconds(10);
opts.Strategy = TimeoutStrategy.Optimistic; // or Pessimistic
opts.OnTimeout = context =>
{
_logger.LogWarning("Operation timed out after {Duration}", context.Timeout);
return Task.CompletedTask;
};
})
.Build();

Timeout Strategies

Optimistic (Default)

Relies on the operation observing the CancellationToken. The token is cancelled after the timeout, and the operation is expected to honour it.

// Works well — HttpClient respects cancellation
await _httpClient.GetStringAsync(url, ct);

// Works well — EF Core respects cancellation
await _dbContext.Products.ToListAsync(ct);
note

Use Optimistic when your operations accept and honour CancellationToken. This is the preferred strategy.

Pessimistic

Forcibly terminates the operation by running it on a background thread and abandoning it when the timeout fires. Use this for operations that do not accept or honour CancellationToken.

var policy = ResiliencePolicy.Create()
.Timeout(opts =>
{
opts.Timeout = TimeSpan.FromSeconds(5);
opts.Strategy = TimeoutStrategy.Pessimistic;
})
.Build();

// Even if the operation doesn't accept ct, it will be abandoned after 5s
await policy.ExecuteAsync<string>(_ =>
{
return Task.FromResult(LegacySynchronousCall());
});
warning

With Pessimistic strategy, the abandoned operation continues running in the background even after timeout. This can cause resource leaks. Use with caution.

Combining with Retry

When combined with Retry, the timeout applies per attempt (not total):

var policy = ResiliencePolicy.Create()
.Retry(3)
.Timeout(TimeSpan.FromSeconds(5)) // 5s per attempt, not 15s total
.Build();

Difference from IHasTimeout

FeatureIHasTimeoutResilience Timeout
WherePipeline behaviorResilience policy
ConfigurationOn the request classOn the policy builder
Applies toAll requests of that typeAny ExecuteAsync call
ExceptionTimeoutExceptionTimeoutException
Use caseDeclarative per-requestDynamic/programmatic