Skip to content
Steve Bohlen edited this page Sep 4, 2016 · 4 revisions

Overview

The behavior of Proteus.Retry when invoking your method is governed by the application of a specific retry policy (an instance of the Proteus.RetryPolicy class) that is configured and then applied to the retry instance before the invocation of your specific method. This RetryPolicy enables control over the number of retries to attempt before failure, the maximum time-out for all combined retry attempts, and other behaviors of the retry process.

Creating a RetryPolicy Instance

To create an instance of RetryPolicy, simply call new on its default constructor:

var policy = new RetryPolicy();

Configuring a RetryPolicy Instance

Once you have created a new RetryPolicy instance, it can be configured by setting one or more of its properties as follows:

MaxRetries Property

  • Description: The maximum number of retries to attempt before throwing a MaxRetryCountExceededException exception.
  • Default: 0 (attempt to invoke once, no retries)
  • Constraints: value >= 0

Usage

policy.MaxRetries = 10; //retry up to 10 times before failing

MaxRetryDuration Property

  • Description: The maximum duration permitted for all combined retry attempts before throwing a MaxRetryDurationExpiredException exception.
  • Default: TimeSpan.FromSeconds(0) (the retry process is given infinite time to complete all attempts)
  • Constraints: none

Usage

policy.MaxRetryDuration = TimeSpan.FromSeconds(10); //all retry attempts will have a combined 10s to complete before failing

RetryDelayInterval Property

  • Description: The duration to delay/pause between successive retry attempts.
  • Default: TimeSpan.FromSeconds(0) (subsequent attempts are immediate)
  • Constraints: none
  • Note: this value will be overridden if RetryDelayIntervalProvider is non-null (see RetryDelayIntervalProvider Property below)

Usage

policy.RetryDelayInterval = TimeSpan.FromSeconds(10); //pause 10s between successive retry attempts

RetryDelayIntervalProvider Property

  • Description: Method that provides a TimeSpan return value. Supports variable (changing) RetryDelayInterval over successive retry attempts (e.g., exponential increasing intervals)
  • Default: null (no provider, value of RetryDelayInterval property will govern retry delay values)
  • Constraints: Any method that matches the signature Func<TimeSpan>

Usage

/*
    This sample class doubles the returned delay interval each time its
    called, permitting a steadily increasing 'back-off' interval so that
    e.g. a remote service is not 'swamped' by rapid-fire successive
    retry attempts but is instead given 'breathing room' to stabilize
    before its called again by Proteus.Retry.

    Delays between successive retries will be 1, 2, 4, 8, 16, 32, etc. seconds
*/
public class DoublingRetryDelayIntervalProvider
{
    private TimeSpan _interval = TimeSpan.FromSeconds(1);

    public TimeSpan Interval
    {
        get { return _interval; }
    }

    public TimeSpan DoublePriorInterval()
    {
        var currentInterval = Interval;
        _interval = TimeSpan.FromTicks(_interval.Ticks * 2);
        return currentInterval;
    }
}


//elsewhere in your code...
var doublingProvider = new DoublingRetryDelayIntervalProvider();

var policy = new RetryPolicy();
policy.RetryDelayIntervalProvider = doublingProvider.DoublePriorInterval();

Controlling Exception Handling Retry Behavior

RetryPolicy can be used to control which exceptions thrown by your invoked method are to be considered 'expected' and will trigger a subsequent retry attempt.

Registering Exceptions

You can use the .RegisterRetriableException<T> method to register each of these exceptions individually with your RetryPolicy instance. You can also use the .RegisterRetriableExceptions(IEnumerable<Type>) method to register multiple Exception types.

Any exceptions not registered with the RetryPolicy will not be handled by Proteus.Retry and will be thrown back to your own calling code to handle.

In the following example, Proteus.Retry will consider IOException to be 'expected' and will catch it and retry again. All other exceptions will be rethrown for your own code to catch/handle accordingly:

policy.RegisterRetriableException<IOException>();

In the following example, multiple Exception types are registered at the same time by registering a collection of exception types to 'expect':

var retriableExceptions = new List<Type>();
retriableExceptions.Add(typeof(IOException));
retriableExceptions.Add(typeof(ArgumentException));

policy.RegisterRetriableExceptions(retriableExceptions);

Note that both of these exception registration methods will throw an exception if the types passed to them do not derive from System.Exception.

Handling Exception Inheritance Hierarchies

By default, Proteus.Retry will take into account the inheritance hierarchy for registered exceptions, meaning that all subclasses of any registered exception(s) are considered to be registered as well. This grants you wide latitude in how you instruct the RetryPolicy to respond to exceptions.

In the following example, this single line instructs Proteus.Retry that all exceptions should be caught and retried because in .NET all exceptions must derive from the single System.Exception base class:

policy.RegisterRetriableException<Exception>();

Thoughtful use of inheritance in deriving your own custom exceptions can offer great flexibility in such cases.

Ignoring Exception Inheritance Hierarchies

There are times when you do not want a RetryPolicy to respect the inheritance hierarchy of registered exceptions. RetryPolicy exposes the .IgnoreInheritanceForRetryExceptions property that enables you to control this behavior. In the following example, only the explicit ArgumentException type will be caught and retried. Any Exceptions derived from ArgumentException will be rethrown by Proteus.Retry and must be caught/handled by your own calling code:

policy.IgnoreInheritanceForRetryExceptions = true;
policy.RegisterRetriableException<ArgumentException>();

Determining Whether an Exception is Registered

In order that you can determine with certainty whether or not a specific Exception instance or Exception type is considered 'expected' (retriable), RetryPolicy provides two methods that can be used to interrogate its exception handling configuration at runtime as follows:

//if the instance of the exception is a type that is registered...
if (policy.IsRetriableException(someException))
{
    //do something
}

//if the IOException type is registered...
if (policy.IsRetriableException<IOException>())
{
    //do something
}

These two methods fully respect the current setting of the .IgnoreInheritanceForRetryExceptions property so they can be used to interrogate the exact manner in which an exception can be expected to be processed by Proteus.Retry.

Applying RetryPolicy to a Retry Instance

Once created and configured, a RetryPolicy can be applied to an instance of the Retry class either via its constructor or by setting the .Policy property on the Retry class. There is no functional difference between these two patterns, each is provided to suit your own style of coding.

Example: Setting RetryPolicy via the Retry Constructor

var policy = new RetryPolicy();
var retry = new Retry(policy);

//TODO: use 'retry' to invoke your method

Example: Setting RetryPolicy via the Policy Property

var policy = new RetryPolicy();
var retry = new Retry();
retry.Policy = policy;

//TODO: use 'retry' to invoke your method