I might be late to the party, but let me put my 2 cents here.
user1672994's suggested solution will not work for SendEventAsync, because the policy definition is created for sync function whereas your to-be-decorated one is async.
There is simple fix for that: use WaitAndRetryAsync instead.
I would also suggest to extract your should trigger retry delegate into a separate method to make your policy definition short and concise like this:
var retryPolicy = Policy
.Handle<Exception>(ShouldTriggerRetry)
.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(3));
bool ShouldTriggerRetry(Exception ex)
{
var errorMessage = ex.InnerException?.Message;
if (string.IsNullOrEmpty(errorMessage)) return false;
var error = JsonConvert.DeserializeObject<dynamic>(errorMessage);
var shouldTrigger = error?.errorCode == 403002;
if(!shouldTrigger) _logger.LogError($"Other error occurred : {error}");
return shouldTrigger;
}
One final thought: As I can see in your code you have a finally block. If you need to do some cleanup between the retry attempts then you should do that inside onRetry or onRetryAsync delegate (depends on your cleanup code: sync or async).
var retryPolicy = Policy
.Handle<Exception>(ShouldTriggerRetry)
.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(3), SyncCleanup);
//...
void SyncCleanup(Exception ex, TimeSpan sleep)
{
//...
}
var retryPolicy = Policy
.Handle<Exception>(ShouldTriggerRetry)
.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(3), AsyncCleanup);
//...
Task AsyncCleanup(Exception ex, TimeSpan sleep)
{
//...
}
WaitAndRetryAsync has 19 overloads so, you might need to scrutinise them if you need to access for example the retry counter or the Context in your onRetry(Async) delegate.
UPDATE #1
Where should I put await deviceClient.SendEventAsync(new Message())
In case of Polly you define a policy and then you use it. The above code was just the definition. The usage looks like this:
await retryPolicy.ExecuteAsync(async () => await deviceClient.SendEventAsync(new Message()));
or like this
await retryPolicy.ExecuteAsync(() => deviceClient.SendEventAsync(new Message()));
Which block I should put my code plus after 3 fail I want to log message and initialize some variable
If all your attempts fail (4 in your case: the initial attempt + 3 retries) then the retry policy will throw the original exception
try
{
await retryPolicy.ExecuteAsync(async () => await deviceClient.SendEventAsync(new Message()));
}
catch(Exception ex)
{
_logger.LogError(ex, "Operation failed 4 times");
}