2

I have the following method which has a retriable network call. The retry policy is specified for a specific exception.

public async Task<MyResponse> GetRecords(MyRequest request)
{
    try
    {
        RetryPolicy retryPolicy = Policy.Handle<MyException>(ex => ex.ErrorCode == ErrorCode.MySpecificErrorCode)
            .Retry(MAX_RETRY_COUNT, onRetry: (exception, retryCount) =>
            {
                log($"Retrying for {retryCount} times due to my error that I want to retry");
            });
        return await retryPolicy.Execute(async () =>
                await OtherService.NetworkCall(request).ConfigureAwait(false))
                .ConfigureAwait(false);
    }
    catch (Exception ex)
    {
        log(ex);
        throw;
    }
}

This is the unit test.

[TestMethod()]
public async Task TestRetry()
{
    OtherServiceMock.SetupSequence(x => x.NetworkCall(It.IsAny<MyRequest>()))
        .ThrowsAsync(new MyException(ErrorCode.MySpecificErrorCode, ExceptionMessage.MySpecificErrorMessage)) //this is getting thrown 
        .ThrowsAsync(new Exception());

    MyRequest request = new MyRequest();
    
    try
    {
        var response = await new MyClassMock.GetRecords(request).ConfigureAwait(false);
    }
    catch(Exception ex)
    {
        log("Event","Event");
    }
}

The first exception is getting thrown instead of the second one. I have tweaked the max retry number, it didn't help. What am I doing wrong here?

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
atprra
  • 114
  • 5
  • Hello @atprra first of all ensure that the comparison you are doing on the exception error code is working the way you expect. What is the type of the ErrorCode property on the MyException class ? Is that an integer ? A string ? A custom class ? – Enrico Massone Sep 27 '22 at 12:16
  • 2
    Second point, have you already tried to use ExecuteAsync instead of Execute ? Based on the [polly documentation](https://github.com/App-vNext/Polly#asynchronous-support) you are expected to use ExecuteAsync for the asynchronous execution of a policy – Enrico Massone Sep 27 '22 at 12:17
  • Thanks @EnricoMassone, the problem is solved. I was supposed to use the async functions. – atprra Sep 27 '22 at 12:27

2 Answers2

2

Just to make it clear, if you want to decorate a sync function with retry then you have to use one of the following policy builder methods:

  • Retry,
  • RetryForever,
  • WaitAndRetry,
  • WaitAndRetryForever

In this case the policy will be a RetryPolicy, which implements the ISyncPolicy interface. So, you can call the Execute which is NOT awaitable.

If you want to decorate an async function with retry then you have to use one of the following policy builder methods:

  • RetryAsync,
  • RetryForeverAsync,
  • WaitAndRetryAsync,
  • WaitAndRetryForeverAsync

In this case the policy will be a AsyncRetryPolicy, which implements the IAsyncPolicy interface. So, you can call the ExecuteAsync which is awaitable.


Here I have detailed how to choose between these method variants: https://stackoverflow.com/a/73095879/13268855

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
0

Your mock setup is for x.GetRecord

OtherServiceMock.SetupSequence(x => x.GetRecord(It.IsAny<MyRequest>()))
    .ThrowsAsync(new MyException(ErrorCode.MySpecificErrorCode, ExceptionMessage.MySpecificErrorMessage)) //this is getting thrown 
    .ThrowsAsync(new Exception());

But the part you want polly to retry on is OtherService.NetworkCall

return await retryPolicy.Execute(async () =>
                    await OtherService.NetworkCall(request).ConfigureAwait(false))
                    .ConfigureAwait(false);

Your SetupSequence should be mocking NetworkCall

OtherServiceMock.SetupSequence(x => x.NetworkCall(It.IsAny<MyRequest>()))
    .ThrowsAsync(new MyException(ErrorCode.MySpecificErrorCode, ExceptionMessage.MySpecificErrorMessage)) //this is getting thrown 
    .ThrowsAsync(new Exception());
Neil
  • 11,059
  • 3
  • 31
  • 56
  • That was a typo in my question. Initially I had both the calls as GetRecord and figured that might cause some confusion. – atprra Sep 27 '22 at 11:56