Home > Mobile >  Polly RetryForever isn't retrying
Polly RetryForever isn't retrying

Time:05-11

I'm, trying to check a simple RetryForever of Polly

class Program
{
    public static void Main()
    {
           
        int i = 0;
        var _retryPolicy = Policy
       .Handle<Exception>()
       .RetryForever();

        _retryPolicy.Execute(async () =>
        {
            Console.WriteLine(i);
            i  ;
            int.Parse("something");
        });

        Console.ReadLine();
    }
}   

A you can see I've created a variable i to track the number of the executions

Excepted Result: print i = 0, 1, 2 etc

Actual Result: print i = 0

CodePudding user response:

I've checked your code and you had right, its stops on 0.

But when i removed async keyword (u didnt have any async operation here) the code runs correct.

So, try this:

using Polly;

int i = 0;
var _retryPolicy = Policy
  .Handle<Exception>()
  .RetryForever();

_retryPolicy.Execute(() =>
{
  Console.WriteLine(i);
  i  ;
  int.Parse("something");
  return Task.CompletedTask;
});

CodePudding user response:

I might be late to the party but let me share my wisdom.


When you define a policy then you have to know upfront that you want to decorate a sync or an async method/function.

Retry policy for sync method

Policy
  .Handle<Exception>()
  .RetryForever();

Retry policy for async method

Policy
  .Handle<Exception>()
  .RetryForeverAsync();
  • In the former case you have an Execute method which could anticipate
    • an Action (a method without return value)
    • or an Func<TResult> (a method with TResult typed return value)
    • and there are a dozen of other overloads
  • In the latter case you have an ExecuteAsync method which could anticipate
    • a Func<Task> (an async method without return value)
    • or a Func<Task<TResult>> (an async method with TResult typed return value)
    • and there are a dozen of other overloads

Retry policy for sync function

Policy<int>
  .Handle<Exception>()
  .RetryForever();

Retry policy for async function

Policy<int>
  .Handle<Exception>()
  .RetryForeverAsync();

In these cases your to be decorated code should return an int.

So the Execute anticipates a Func<int> delegate

policy.Execute(() => { ... return 42; });

and the ExecuteAsync anticipates a Func<Task<int>> delegate.

await policy.Execute(async () => { ... return Task.FromResult(42); });

Back to your code

Since you did not made any constraint to the return type during the policy definition that's why you could pass a Func which returns a Task (Execute(async () => { ...})

So, your Execute returns a Task which is not awaited

Let's await it

await _retryPolicy.Execute(async () =>
{
    Console.WriteLine(i);
    i  ;
    int.Parse("something");
});

If you await the returned Task then it will throw a FormatException. But that Exception is thrown by the await which is outside of the Execute's delegate. So, it will NOT trigger a retry.

Option A for fix

_retryPolicy.Execute(() =>
{
    Console.WriteLine(i);
    i  ;
    int.Parse("something");
});

By removing async your delegate will be an Action which will throw the FormatException which could trigger the retry policy

Option B for fix

var _retryPolicy = Policy<int>
  .Handle<Exception>()
  .RetryForeverAsync();

await _retryPolicy.ExecuteAsync(async () =>
{
    Console.WriteLine(i);
    i  ;
    int.Parse("something");
});

If you define your policy for async method and you await the ExecuteAsync then it will work as well. Why? Because ExecuteAsync awaits the provided delegate even if you did not define the delegate as async

await _retryPolicy.ExecuteAsync(() =>
{
    Console.WriteLine(i);
    i  ;
    int.Parse("something");
    return Task.CompletedTask; //This code is never reached
});
  • Related