/// <summary> /// Execute a given function a number of times, based on retry configuration parameters. /// </summary> public static Task ExecuteWithRetriesAsync( Func <int, Task> userDefinedFunction, int maxNumErrorTries, Func <Exception, int, bool> retryExceptionFilter, TimeSpan maxExecutionTime, IBackOffProvider onErrorBackOff = null) { async Task <bool> WrapFunction(int i) { await userDefinedFunction(i); return(true); } return(DoExecuteWithRetriesAsync( WrapFunction, 0, 0, maxNumErrorTries, maxExecutionTime, DateTime.UtcNow, null, retryExceptionFilter, null, onErrorBackOff)); }
/// <summary> /// Execute a given function a number of times, based on retry configuration parameters. /// </summary> public static Task <T> ExecuteWithRetriesAsync <T>( Func <int, Task <T> > userDefinedFunction, int maxNumErrorTries, Func <Exception, int, bool> retryExceptionFilter, TimeSpan maxExecutionTime, IBackOffProvider onErrorBackOff = null) { return(ExecuteWithRetriesAsync <T>( userDefinedFunction, 0, maxNumErrorTries, null, retryExceptionFilter, maxExecutionTime, null, onErrorBackOff)); }
/// <summary> /// Execute a given function a number of times, based on retry configuration parameters. /// </summary> /// <param name="userDefinedFunction">Function to execute</param> /// <param name="maxNumSuccessTries">Maximal number of successful execution attempts. /// ExecuteWithRetries will try to re-execute the given function again if directed so by retryValueFilter. /// Set to -1 for unlimited number of success retries, until retryValueFilter is satisfied. /// Set to 0 for only one success attempt, which will cause retryValueFilter to be ignored and the given function executed only once until first success.</param> /// <param name="maxNumErrorTries">Maximal number of execution attempts due to errors. /// Set to -1 for unlimited number of error retries, until retryExceptionFilter is satisfied.</param> /// <param name="retryValueFilter">Filter function to indicate if successful execution should be retied. /// Set to null to disable successful retries.</param> /// <param name="retryExceptionFilter">Filter function to indicate if error execution should be retied. /// Set to null to disable error retries.</param> /// <param name="maxExecutionTime">The maximal execution time of the ExecuteWithRetries function.</param> /// <param name="onSuccessBackOff">The back off provider object, which determines how much to wait between success retries.</param> /// <param name="onErrorBackOff">The back off provider object, which determines how much to wait between error retries.</param> /// <returns></returns> public static Task <T> ExecuteWithRetriesAsync <T>( Func <int, Task <T> > userDefinedFunction, int maxNumSuccessTries, int maxNumErrorTries, Func <T, int, bool> retryValueFilter, Func <Exception, int, bool> retryExceptionFilter, TimeSpan maxExecutionTime = default, IBackOffProvider onSuccessBackOff = null, IBackOffProvider onErrorBackOff = null) { return(DoExecuteWithRetriesAsync <T>( userDefinedFunction, 0, maxNumSuccessTries, maxNumErrorTries, maxExecutionTime, DateTime.UtcNow, retryValueFilter, retryExceptionFilter, onSuccessBackOff, onErrorBackOff)); }
private static async Task <T> DoExecuteWithRetriesAsync <T>( Func <int, Task <T> > userDefinedFunction, int callCounter, int maxNumSuccessTries, int maxNumErrorTries, TimeSpan maxExecutionTime, DateTime startExecutionTime, Func <T, int, bool> retryValueFilter = null, Func <Exception, int, bool> retryExceptionFilter = null, IBackOffProvider onSuccessBackOff = null, IBackOffProvider onErrorBackOff = null) { onSuccessBackOff ??= DefaultBackOffProvider; onErrorBackOff ??= DefaultBackOffProvider; var result = default(T); ExceptionDispatchInfo lastExceptionInfo = null; bool needRetry; do { needRetry = false; if (maxExecutionTime != InfiniteTimespan && maxExecutionTime != default) { var now = DateTime.UtcNow; if (now - startExecutionTime > maxExecutionTime) { if (lastExceptionInfo == null) { throw new TimeoutException( $"ExecuteWithRetries has exceeded its max execution time of {maxExecutionTime}. Now is {LogFormatter.PrintDate(now)}, started at {LogFormatter.PrintDate(startExecutionTime)}, passed {now - startExecutionTime}"); } lastExceptionInfo.Throw(); } } var counter = callCounter; try { callCounter++; result = await userDefinedFunction(counter); lastExceptionInfo = null; if (callCounter < maxNumSuccessTries || maxNumSuccessTries == InfiniteRetries) // -1 for infinite retries { if (retryValueFilter != null) { needRetry = retryValueFilter(result, counter); } } if (needRetry) { var delay = onSuccessBackOff?.Next(counter); if (delay.HasValue) { await Task.Delay(delay.Value); } } } catch (Exception exc) { needRetry = false; if (callCounter < maxNumErrorTries || maxNumErrorTries == InfiniteRetries) { if (retryExceptionFilter != null) { needRetry = retryExceptionFilter(exc, counter); } } if (!needRetry) { throw; } lastExceptionInfo = ExceptionDispatchInfo.Capture(exc); var delay = onErrorBackOff?.Next(counter); if (delay.HasValue) { await Task.Delay(delay.Value); } } } while (needRetry); return(result); }