-
Notifications
You must be signed in to change notification settings - Fork 0
Retry Policies
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.
To create an instance of RetryPolicy
, simply call new
on its default constructor:
var policy = new RetryPolicy();
Once you have created a new RetryPolicy instance, it can be configured by setting one or more of its properties as follows:
- 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
- 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
- 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
- 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();
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.
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
.
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.
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>();
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
.
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.
var policy = new RetryPolicy();
var retry = new Retry(policy);
//TODO: use 'retry' to invoke your method
var policy = new RetryPolicy();
var retry = new Retry();
retry.Policy = policy;
//TODO: use 'retry' to invoke your method