Skip to content
reisenberger edited this page Feb 18, 2017 · 35 revisions

Retry

Syntax

RetryPolicy retry = Policy
  .Handle<HttpException>()
  .Retry(3);

The above example will create a retry policy which will retry up to three times, an action which fails with an exception handled by the Policy.

For full retry syntax and overloads (including retry-forever, wait-and-retry, and related variants), see https://github.com/App-vNext/Polly#retry.

Syntax examples given are sync; comparable async overloads exist for asynchronous operation: see readme and wiki.

How Polly Retry works

retry operation

When an action is executed through the policy:

  • The retry policy attempts the action passed in the .Execute(…) (or similar) delegate.
    • If the action executes successfully, the return value (if relevant) is returned and the policy exits.
    • If the action throws an unhandled exception, it is rethrown and the policy exits: no further tries are made.
  • If the action throws a handled exception, the policy:
    • Counts the exception
    • Checks whether another retry is permitted.
      • If not, the exception is rethrown and the policy terminates.
      • If another try is permitted, the policy:
        • Raises the onRetry delegate (if configured)
        • In the case of a wait-and-retry policy, calculates the duration to wait from the IEnumerable<TimeSpan> or Func<int, TimeSpan> configured, and waits.
        • Returns to the beginning of the cycle, to retry executing the action again.

Overall number of attempts

The overall number of attempts that may be made to execute the action is one plus the number of retries configured. For example, if the policy is configured .Retry(3), up to four attempts are made: the initial attempt, plus up to three retries.

Exponential backoff

A common retry strategy is exponential backoff: this allows for retries to be made initially quickly, but then at progressively longer intervals, to avoid hitting a subsystem with too many simultaneous calls if the subsystem may be struggling.

Exponential backoff can be achieved by configuring waits-between-retries manually:

Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetry(new[]
  {
    TimeSpan.FromSeconds(1),
    TimeSpan.FromSeconds(2),
    TimeSpan.FromSeconds(4)
  });

or by calculation:

Policy
  .Handle<DivideByZeroException>()
  .WaitAndRetry(3, retryAttempt => 
    TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) 
  );

Other users for Retry

While the original premise of retry policies is to handle transient faults, other uses are possible. We have heard of retry policies used to make BDD tests more robust (retrying actions that might initially fail only because some page element was yet to load). Another common pattern is to maintain authorisation against a third-party system, where that authorisation periodically lapses.

var authorisationEnsuringPolicy = Policy
  .Handle<SomeAuthorizationException>()
  .Retry(1, onRetry: (e, i) => RefreshAuthorization());

authorisationEnsuringPolicy.Execute(() => DoSomethingThatRequiresAuthorization());

PolicyWrap allows you to use the same family of policy (retry in this case) twice in the same PolicyWrap, so it is possible to wrap a single call site in an authentication-ensuring retry such as above, as well as in a separate transient-fault handling retry policy.

Thread-safety

Each call to .Execute(…) (or similar) through a retry policy maintains its own private state. A retry policy can therefore be re-used safely in a multi-threaded environment.

The internal operation of the retry policy is thread-safe, but this does not magically make delegates you execute through the policy thread-safe: if the delegates you execute through the policy are not thread-safe, they remain not thread-safe.

Clone this wiki locally