Skip to content

Resilience

Stanislav Molchanovskiy edited this page Nov 15, 2021 · 12 revisions

Requests can end with errors for many reasons, so it is necessary to ensure resilience. By default, a request is executed once without retries. If the request ended with an unsuccessful HTTP status code and the returned method value is not HTTP response, an exception will be thrown. To change the logic of retries, you can use the WithResilience methods.

Common policy

Use the WithFullResilience method to retry requests for any methods:

IMyClient myClient = NClientGallery.Clients.GetRest()
    .For<IMyClient>(host: "http://localhost:8080")
    .WithFullResilience(
        maxRetries: 4, 
        getDelay: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)),
        shouldRetry: responseContext => !responseContext.Response.IsSuccessStatusCode)
    .Build();

The parameters maxRetries, getDelay and shouldRetry are optional. By default, 3 attempts are used with a quadratic increase in the delay between attempts for responses with unsuccessful codes. To use retries for safe methods (GET, HEAD, OPTIONS), use the WithSafeResilience method. To use retries for all methods except POST, use the WithIdempotentResilience method.

Specific policy for a method

Set specific resilience policy for a method using the WithResilience method:

IMyClient myClient = NClientGallery.Clients.GetRest()
    .For<IMyClient>(host: "http://localhost:8080")
    .WithResilience(builder => builder
        .ForMethod(client => ((Func<Entity, Task>)x.PostAsync).Use(maxRetries: 4))
    .Build();

Policy combinations

You can flexibly configure resilience using a combination of policies:

IMyClient myClient = NClientGallery.Clients.GetRest()
    .For<IMyClient>(host: "http://localhost:8080")
    .WithFullResilience(maxRetries: 4)
    .WithResilience(builder => builder
        .ForMethod(client => ((Func<Entity, Task>)x.PostAsync).DoNotUse())
    .Build();

Polly policy

For more complex cases, you can use the Polly library:

var retryPolicy = Policy<ResponseContext>
    .HandleResult(x => !x.HttpResponse.IsSuccessful)
        .Or<Exception>()
    .WaitAndRetryAsync(
        retryCount: 2,
        sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));

IMyClient myClient = NClientGallery.Clients.GetRest()
    .For<IMyClient>(host: "http://localhost:8080")
    .WithResilience(builder => builder
        .ForAllMethods().UsePolly(retryPolicy))
    .Build();

You can also create your own implementation of the IResiliencePolicyProvider and pass it to the Use method.

Runtime policy change

You may need to set policies in runtime. To create or change a policy for an already created client use the Invoke method:

public class MyResiliencePolicyProvider : IResiliencePolicyProvider { ... }
...
await myClient.AsResilient().Invoke(x => x.PostAsync(id), new MyResiliencePolicyProvider());

Please note, the client interface must inherit the INClient interface.

Clone this wiki locally