Home > Mobile >  retry insert 3 times only when error code 403002 occurs
retry insert 3 times only when error code 403002 occurs

Time:07-30

I came to know through Polly I can re-try the execution up to multiple configurable time like below example,

 Policy
.Handle<SqlException>(ex => ex.Number == 1205)
.Or<ArgumentException>(ex => ex.ParamName == "example")
.WaitAndRetry(3, _ => TimeSpan.FromSeconds(3))
.Execute(DoSomething);

In my case below I am doing JSON serialization and all to find the error code 403002 and only on this error I want to re-try my code await deviceClient.SendEventAsync(new Message()); with a configuration time.

How to achieve this with Polly?

try
        {
            await deviceClient.SendEventAsync(new Message());
        }
        catch (Exception ex)
        {
            var errorMessage = ex.InnerException?.Message;

            if (errorMessage != null)
            {
                dynamic error = JsonConvert.DeserializeObject(errorMessage);
                if (error != null && error.errorCode == 403002)
                {
                    // want to re-try again 'await deviceClient.SendEventAsync'
                }
                else
                {
                    _logger.LogError($"Other error occurred : {error}");
                }
            }
        }
        finally
        {
            //
        }

CodePudding user response:

You can Handle the generic Exception and validate the errorCode to return bool value.

Policy
.Handle<Exception>(ex => 
{
    var errorMessage = ex.InnerException?.Message;

    if (errorMessage != null)
    {
         dynamic error = JsonConvert.DeserializeObject(errorMessage);
         if (error != null && error.errorCode == 403002)
         {
             // want to re-try again 'await deviceClient.SendEventAsync'
              return true;
          }
      }
      return false;
})
.WaitAndRetry(3, _ => TimeSpan.FromSeconds(3))
.Execute(DoSomething); 

CodePudding user response:

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 fails (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");
}
  • Related