Reputation: 121
I'm trying to set up Polly in .Net Core 3.1 (Azure Functions v3).
I want to create a Policy in the Startup class which I can inject into functions.
The behaviour that I'm looking for is: It should wait And retry 3 times - if the final try fails - it should catch and log the exception (Serilog) otherwise just continue.
At the moment I have the following piece of code:
AsyncRetryPolicy emailRetryPolicy = Policy.Handle<Exception>()
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromMilliseconds(retryAttempt * 500));
As you see I miss the part where it logs the error after 3 tries - and just continue. Any Idea how to do that?
Serilog is also added in Startup class (Partial code shown)
builder.Services.AddLogging(al => al.AddSerilog(logger));
I'm going to send an email with MailKit and want to log if it for some reasons fails. It should not stop the program for running.
Upvotes: 6
Views: 7676
Reputation: 2160
Also had that issue and it took me some time to figure this out, so sharing my solution here. It's important to note that the order of policies wrapped matters
My case:
var retryPolicy = Policy
.Handle<EmptyResponseException>()
.Or<Exception>()
.Or<HttpOperationException>()
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: _ => TimeSpan.FromSeconds(3),
onRetryAsync: (e, i) => LogHandler.WarningAsync("Retrying due to " + e.Message + " Try " + i + " next."));
var fallbackPolicy = Policy.Handle<EmptyResponseException>()
.Or<Exception>()
.Or<HttpOperationException>()
.FallbackAsync
(
async ct =>
{
await Task.FromResult(true);
await LogHandler.CriticalAsync("i'm a fallback action");
/* do something else async if desired */
},
async ex =>
{
await LogHandler.CriticalAsync("i'm an actual fallback");
//throw ex; <<< if you throw here, the fallback action won't happen
}
);
_policy = Policy.WrapAsync(fallbackPolicy, retryPolicy);
Result:
It doesn't quite matter what the fallbackPolicy
or retryPolicy
are in my case, but if I were to use this instead:
_policy = Policy.WrapAsync(retryPolicy, fallbackPolicy);
We'd get the fallback execute first, and the retry wouldn't work:
Upvotes: 3
Reputation: 22829
The retry policy works in the following way:
Depending on your business logic, which you would like to wrap with this retry policy, you have the following two options:
Policy.WrapAsync
, but be aware of their order.Sample fallback policy:
var fallbackPolicy = Policy<YourResponse>
.Handle<Exception>()
.FallbackAsync( (ct) =>
{
logger.LogError("Operation has been failed after several retries ...");
return Task.FromResult(new YourResponse { ... });
});
var combinedPolicy = Policy.WrapAsync(fallbackPolicy, retryPolicy);
onRetryAsync
callback of the AsyncRetryPolicy
Sample retry policy:
var retryPolicy = Policy
.Handle<Exception>()
.WaitAndRetryAsync(
settings.RetryCount,
_ => TimeSpan.FromMilliseconds(settings.SleepDurationInMilliseconds),
onRetryAsync: (ex, count, context) =>
logger.LogError("Operation has been failed after several retries ...");
);
I would like to emphasize that retry policy is designed for idempotent operations. Any service call, which might send an e-mail as a side-effect is not an idempotent operation.
Upvotes: 5