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
Executemethod which could anticipate- an
Action(a method without return value) - or an
Func<TResult>(a method withTResulttyped return value) - and there are a dozen of other overloads
- an
- In the latter case you have an
ExecuteAsyncmethod which could anticipate- a
Func<Task>(an async method without return value) - or a
Func<Task<TResult>>(an async method withTResulttyped return value) - and there are a dozen of other overloads
- a
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
});
