// 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; } } }