// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Data.Entity.Infrastructure
{
using System.Collections.Generic;
using System.Data.Entity.Resources;
using System.Linq;
///
/// A retry policy with exponentially increasing delay between retries.
///
///
/// The following formula is used to calculate the delay after retryCount number of attempts:
/// min(minDelay + coefficient * random(1, maxRandomFactor) * (exponentialBase ^ retryCount - 1), maxDelay)
/// The retryCount starts at 0.
/// The random factor distributes uniformly the retry attempts from multiple parallel actions failing simultaneously.
/// The coefficient determines the scale at wich the delay is increased while the exponentialBase
/// sets the speed of the delay increase.
///
public class ExponentialRetryDelayStrategy : IRetryDelayStrategy
{
private readonly int _maxRetryCount;
private readonly TimeSpan _minDelay;
private readonly TimeSpan _maxDelay;
private readonly double _maxRandomFactor;
private readonly double _exponentialBase;
private readonly TimeSpan _coefficient;
///
/// The default number of retry attempts.
///
public static readonly int DefaultMaxRetryCount = 5;
///
/// The default maximum random factor.
///
public static readonly double DefaultRandomFactor = 1.1;
///
/// The default base for the exponential function used to compute the delay between retries.
///
public static readonly double DefaultExponentialBase = 2;
///
/// The default coefficient for the exponential function used to compute the delay between retries.
///
public static readonly TimeSpan DefaultCoefficient = TimeSpan.FromSeconds(1);
///
/// The default maximum time delay between retries.
///
public static readonly TimeSpan DefaultMaxDelay = TimeSpan.FromSeconds(30);
///
/// The default minimum time delay between retries.
///
public static readonly TimeSpan DefaultMinDelay = TimeSpan.FromSeconds(0);
private readonly List _exceptionsEncountered = new List();
private readonly Random _random = new Random();
///
/// Initializes a new instance of the class.
///
public ExponentialRetryDelayStrategy()
: this(DefaultMaxRetryCount, DefaultMinDelay, DefaultMaxDelay, DefaultRandomFactor, DefaultExponentialBase, DefaultCoefficient)
{
}
///
/// Initializes a new instance of the class.
///
/// The maximum number of retry attempts.
/// The minimum delay in milliseconds between retries, must be nonnegative.
/// The maximum delay in milliseconds between retries, must be equal or greater than .
/// The maximum random factor, must not be lesser than 1.
/// The base for the exponential function used to compute the delay between retries, must be positive.
/// The coefficient for the exponential function used to compute the delay between retries, must be nonnegative.
public ExponentialRetryDelayStrategy(
int maxRetryCount, TimeSpan minDelay, TimeSpan maxDelay, double maxRandomFactor, double exponentialBase, TimeSpan coefficient)
{
if (maxRetryCount < 0.0)
{
throw new ArgumentOutOfRangeException("maxRetryCount");
}
if (minDelay.TotalMilliseconds < 0.0)
{
throw new ArgumentOutOfRangeException("minDelay");
}
if (minDelay.TotalMilliseconds > maxDelay.TotalMilliseconds)
{
throw new ArgumentOutOfRangeException("maxDelay", Strings.ExecutionStrategy_MinimumMustBeLessThanMaximum);
}
if (maxRandomFactor < 1.0)
{
throw new ArgumentOutOfRangeException("maxRandomFactor");
}
if (exponentialBase <= 0.0)
{
throw new ArgumentOutOfRangeException("exponentialBase");
}
if (coefficient.TotalMilliseconds < 0.0)
{
throw new ArgumentOutOfRangeException("coefficient");
}
_maxRetryCount = maxRetryCount;
_minDelay = minDelay;
_maxDelay = maxDelay;
_maxRandomFactor = maxRandomFactor;
_exponentialBase = exponentialBase;
_coefficient = coefficient;
}
///
public TimeSpan? GetNextDelay(Exception lastException)
{
_exceptionsEncountered.Add(lastException);
var currentRetryCount = _exceptionsEncountered.Count() - 1;
if (currentRetryCount < _maxRetryCount)
{
var delta = (Math.Pow(_exponentialBase, currentRetryCount) - 1.0)
* (1.0 + _random.NextDouble() * (_maxRandomFactor - 1.0));
var delay = Math.Min(
_minDelay.TotalMilliseconds + _coefficient.TotalMilliseconds * delta,
_maxDelay.TotalMilliseconds);
return TimeSpan.FromMilliseconds(delay);
}
return null;
}
}
}