I am trying to implement retry mechanism on DbUpdateConcurrencyException
(link). There are some ways to that, like add counter or have logic in catch
block to retry (this link shows that).
Here, I am trying Polly
to do that and not getting desired result.
What am I missing here? Current, logic goes to catch block and throws the error instead of retry.
Code:
public class MyService
{
private readonly ILogger<MyService> _logger;
// Using Polly for retry
private readonly RetryPolicy _policy = Policy
.Handle<DbUpdateConcurrencyException>()
.WaitAndRetry(3, _ => TimeSpan.FromSeconds(1));
public MyService(ILogger<MyService> logger)
{
_mapper = mapper;
}
public Task<ServiceResponse<MyDto>> UpsertSpecificationsWithRetryAsync(MyDto myDto, CancellationToken cancellationToken)
{
// Expecting this to be called 3 times in case of failure, every second
return _policy.Execute(async ct => await UpsertSpecificationsAsync(myDto, cancellationToken), cancellationToken);
}
public async Task<ServiceResponse<MyDto>> UpsertSpecificationsAsync(MyDto myDto,
CancellationToken cancellationToken)
{
try
{
// To test the retry mechanism
throw new DbUpdateConcurrencyException();
}
catch (Exception exception)
{
_logger.Error(message: $"Exception while upserting options. AtdDto: {{@atdDto}} Exception: {{@exception}}",
args: new object[] { myDto, exception });
return GetExceptionServiceResponse<MyDto>(exception.GetBaseException().Message);
}
}
}
try changing this :
private readonly RetryPolicy _policy = Policy
.Handle<DbUpdateConcurrencyException>()
.WaitAndRetry(3, _ => TimeSpan.FromSeconds(1));
to this :
private readonly AsyncRetryPolicy _policy = Policy
.Handle<DbUpdateConcurrencyException>()
.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(1));
and this :
public Task<ServiceResponse<MyDto>> UpsertSpecificationsWithRetryAsync(MyDto myDto, CancellationToken cancellationToken)
{
// Expecting this to be called 3 times in case of failure, every second
return _policy.Execute(async ct => await UpsertSpecificationsAsync(myDto, cancellationToken), cancellationToken);
}
to this :
public async Task<ServiceResponse<MyDto>> UpsertSpecificationsWithRetryAsync(MyDto myDto, CancellationToken cancellationToken)
{
// Expecting this to be called 3 times in case of failure, every second
return (await _policy.ExecuteAndCaptureAsync(() => UpsertSpecificationsAsync(myDto, cancellationToken)))?.Result;
}
And finally, if you want to log the error in the catch
block, you should throw
it after loging
try
{
// To test the retry mechanism
throw new DbUpdateConcurrencyException();
}
catch (Exception exception)
{
_logger.Error(message: $"Exception while upserting options. AtdDto: {{@atdDto}} Exception: {{@exception}}",
args: new object[] { myDto, exception });
throw;
///return GetExceptionServiceResponse<MyDto>(exception.GetBaseException().Message);
}
But for loging error, Polly
provides it to you and you can do the following instead of using try catch
:
private readonly AsyncRetryPolicy _policy = Policy
.Handle<DbUpdateConcurrencyException>()
.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(1), (exception, _) =>
{
//log error
});