Search code examples
c#polly

Can a policy throw an exception if the result criteria is never met?


I'd like to use Polly to handle the result of an HTTP API call I need to make. I have the policy defined as:

var r = await Policy
    .HandleResult<DescribeApplicationVersionsResponse>(x => x.ApplicationVersions[0]?.Status != ApplicationVersionStatus.Processed)
    .WaitAndRetryAsync(10, retryAttempt => TimeSpan.FromSeconds(1), onRetry: (describeResponse, timeSpan, context) => {
        Console.WriteLine($"Application version was '{describeResponse.Result.ApplicationVersions[0].Status}', retrying in {timeSpan}");
    })
    .ExecuteAsync(() =>
        BeanstalkClient.DescribeApplicationVersionsAsync(describeApplicationVersionRequest)
    );

If the policy never succeeds (i.e., the result criteria is never met and the retry limit has been passed) I'd like to throw an exception as my application can't continue.

Is it possible to define that through the Polly policy builder extension methods?


Solution

  • If all retries configured for a Polly retry policy are exhausted without the executed delegate succeeding, the retry policy rethrows the exception for which no more retries are permitted, as documented in the Polly readme (foot of retry section) and Polly retry wiki.


    If you want to throw a different exception from the one propagating from the delegate, you could do any of:

    • try/catch and throw something else; (or)
    • use Polly .ExecuteAndCapture(...) to capture, then check, the final execution result, and throw your own custom exception; (or)
    • use PolicyWrap to wrap a FallbackPolicy outside the retry policy, and in the FallbackPolicy, handle the exception from retries-failing and in the fallback action, rethrow a different custom exception.

    EDITED: To reflect the fact that the OP's original policy was handling results, not exceptions. An approach using FallbackPolicy with PolicyWrap could be as follows:

    var retry = Policy
        .HandleResult<DescribeApplicationVersionsResponse>(x => x.ApplicationVersions[0]?.Status != ApplicationVersionStatus.Processed)
        .WaitAndRetryAsync(10, retryAttempt => TimeSpan.FromSeconds(1), onRetry: (describeResponse, timeSpan, context) => {
            Console.WriteLine($"Application version was '{describeResponse.Result.ApplicationVersions[0].Status}', retrying in {timeSpan}");
        });
    
    var fallback = Policy
        .HandleResult<DescribeApplicationVersionsResponse>(x => x.ApplicationVersions[0]?.Status != ApplicationVersionStatus.Processed) // Probably worth factoring the predicate out into a method so that it is only stated once.
        .FallbackAsync(async () => { throw new ReplacementException(); });
    
    var r = await fallback.WrapAsync(retry)
        .ExecuteAsync(() =>
       BeanstalkClient.DescribeApplicationVersionsAsync(describeApplicationVersionRequest)
    );