Skip to content

Quick start

Install the NResilience package:

bash
dotnet add package NResilience
csharp
// One client for the application's lifetime, with the policy already inside it.
private static readonly HttpClient Client = ResilienceHttp.CreateClient();

private static async Task<User?> GetUserAsync(int id, CancellationToken cancellationToken) =>
    await Client.GetFromJsonAsync<User>(requestUri: new Uri(uriString: $"https://api.example.com/users/{id}"), cancellationToken: cancellationToken);

This creates a working retried client. Provide only your own cancellation token.

CreateClient() uses the Resilience.Http preset, which provides:

  • Three attempts
  • Exponential backoff with full jitter
  • A 30-second deadline
  • A 10-second attempt timeout
  • An HTTP classifier that retries 503 responses but not 404 responses

The client's handler handles HTTP-specific logic: it rebuilds the request for every attempt, excludes POST requests from the retry path, and scopes the circuit breaker to the host.

Run any call, not just HTTP

Wrap any asynchronous work - such as a queue read, a database call, or a third-party SDK - using RunAsync.

csharp
var api = Resilience.Default;

var name = await api.RunAsync(attempt => db.ReadNameAsync(id: id, cancellationToken: attempt), cancellationToken: cancellationToken);

The callback provides an attempt token, which differs from the cancellationToken you pass in:

  • attempt is cancelled when the specific attempt hits its AttemptTimeout.
  • cancellationToken cancels the entire operation.

Pass the attempt token into your work to ensure that timed-out attempts actually stop.

TIP

Every execution overload requires a callback that takes a CancellationToken. An analyzer in the package notifies you at build time if you pass the wrong token to your work.

Handle outcomes without exceptions

RunAsync throws the exception encountered during the call. To branch on the outcome instead of catching exceptions, use TryRunAsync.

csharp
var result = await api.TryRunAsync(attempt => FetchAsync(cancellationToken: attempt), cancellationToken: cancellationToken);

if (!result.TryGetValue(value: out var user))
{
    // Why it stopped, and everything that happened on the way.
    Console.WriteLine(value: result.StopReason); // AttemptsExhausted
    Console.WriteLine(value: result.Attempts); // 2 attempts over 1.2ms: Transient IOException (0.6ms), ...
}

Implement your fallback strategy in the if block, such as by serving a cached value or a default.

Next steps

Released under the MIT License.