Retrying a failed operation

One of our main applications involves interacting with a third-party API. Whilst this is generally stable, we have had cases recently where it was throwing random exceptions. We could make the same request with the same parameters multiple times, and sometimes it would work, and sometimes it wouldn’t.

While we left the owners of the third-party API scratching their heads, we realised that the way we handled interaction with such APIs wasn’t robust enough. The first (and most obvious) improvement would simply be to try again.

As this is the sort of thing we’d want to do in a number of places, rather than repeat the same boilerplate code over and over again, we looked for a helper class. This SO answer gave us a starting point, which we simplified to suit our needs. Instead of throwing an AggregateException, we decided to pick just the last exception, leaving us with the following helper…

public static class Retry {
  public static void Do(Action action, int maxTries = 3, int retryMs = 0) {
    Exception e = null;
    for (int attempted = 0; attempted < maxTries; attempted++) {
      try {
        if (attempted > 0) {
          Thread.Sleep(retryMs);
        }
        action();
        return;
      } catch (Exception ex) {
        e = ex;
      }
    }
    throw new Exception($"Exception after {maxTries} attempts, see the inner exception", e);
  }
}

Usage is simple enough. If you are happy with the default values for number of tries and delay before retrying, then it’s nothing more than…

  try {
    Retry.Do(() => DoSomething());
  } catch (Exception ex) {
    // as ever, but safe in the knowledge that we had a good go at it
  }

If the method being called doesn’t take parameters, you could shorten the call to…

    Retry.Do(DoSomething);

There was some discussion in the comments there about throwing the first exception, in which case you could modify the catch block in the Retry.Do helper as follows…

      } catch (Exception ex) {
        if (e == null) {
          e = ex;
        }
      }

I guess what you choose depends on your situation. Whichever way you do it, it simplifies a common requirement.

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.