Пример #1
0
        /// <summary>
        /// Invokes associated handler function for the specified message
        /// </summary>
        /// <param name="messageType"></param>
        /// <param name="messageEnvelope"></param>
        /// <param name="currentRetryCount"></param>
        /// <returns></returns>
        private IHandlerResult PerformHandler(Type messageType, object messageEnvelope, int currentRetryCount)
        {
            try
            {
                return(this.subscriptions[messageType].Invoke(messageEnvelope));
            }
            catch (Exception exceptionThrown)
            {
                const string LogMessageFormat = "Unhandled exception in handler: {0}";

                var exceptionToHandle = exceptionThrown is TargetInvocationException && exceptionThrown.InnerException != null
                    ? exceptionThrown.InnerException
                    : exceptionThrown;
                string exceptionMessage = exceptionToHandle.ToString();

                if (RetryResult.WillRetry(currentRetryCount))
                {
                    // log a warning for retriable messages, since the error may just be transient
                    this.configuration.Logger.WarnFormat(LogMessageFormat, exceptionMessage);
                }
                else
                {
                    this.configuration.Logger.ErrorFormat(LogMessageFormat, exceptionMessage);
                }

                return(new RetryResult(exceptionMessage));
            }
        }
Пример #2
0
        /// <summary>
        /// Retries a function until the predicate evaluates false and the function succeeds or until timeout is reached.
        /// </summary>
        /// <typeparam name="TResult">Return type of the function</typeparam>
        /// <param name="shouldRetry">Predicate that evaluates the results of the function</param>
        /// <param name="function">Function that will be retried</param>
        /// <param name="timeout">Time the action will be retried</param>
        /// <param name="retryInterval">Interval between retries</param>
        /// <param name="cancellationToken">Token to cancel retry operation</param>
        /// <returns>Return of the function</returns>
        public static RetryResults <TResult> While <TResult>(Predicate <TResult> shouldRetry, Func <TResult> function, TimeSpan timeout, TimeSpan?retryInterval = null, CancellationToken cancellationToken = new CancellationToken())
        {
            DateTime start = DateTime.Now;
            RetryResults <TResult> results = new RetryResults <TResult>();

            while (true)
            {
                DateTime retryStart          = DateTime.Now;
                RetryResult <TResult> result = new RetryResult <TResult>();

                try
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                    }

                    result.Value = function();
                    if (!shouldRetry(result.Value))
                    {
                        result.IsCompletedSuccessfully = true;
                    }
                }
                catch (OperationCanceledException canceledException)
                {
                    result.Exception = canceledException;
                    result.IsCompletedSuccessfully = false;
                    result.IsCanceled = true;
                }
                catch (Exception exception)
                {
                    result.Exception = exception;
                    result.IsCompletedSuccessfully = false;
                }
                finally
                {
                    result.Start  = retryStart;
                    result.Finish = DateTime.Now;
                    results.Retries.Add(result);
                }

                if (result.IsCompletedSuccessfully)
                {
                    return(results);
                }

                if (result.IsCanceled)
                {
                    return(results);
                }

                if (IsTimedOut(start, timeout))
                {
                    return(results);
                }

                Thread.Sleep(retryInterval ?? DefaultRetryInterval);
            }
        }
Пример #3
0
        public void SuccessfulFunctionDoesntReturnFallbackValue()
        {
            // Arrange & Act.
            RetryResult <int> result = Retrier.Init()
                                       .WithMsWaitOf(0)
                                       .WithNumberOfRetries(2)
                                       .Invoke(() => 10 / 2)
                                       .WithFallBackValue(1000);

            // Assert.
            Assert.Equal(5, result.Result);
            Assert.True(result.Successful);
            Assert.Equal(1, result.RetryInfo.Executions);
        }
Пример #4
0
        public void CountExceptionsAndRetries()
        {
            // Arrange & Act.
            int zero = 0;
            RetryResult <int> result = Retrier.Init()
                                       .WithMsWaitOf(0)
                                       .WithNumberOfRetries(2)
                                       .Invoke(() => 2 / zero);

            // Assert.
            Assert.Equal(0, result.Result);
            Assert.False(result.Successful);
            Assert.Equal(3, result.RetryInfo.Executions);
            Assert.IsType <DivideByZeroException>(result.RetryInfo.Exceptions.FirstOrDefault());
        }
Пример #5
0
        public void ExpireAfterTotalTimeoutWithRetryForever()
        {
            // Arrange & Act.
            int zero = 0;
            RetryResult <int> result = Retrier.Init()
                                       .WithWaitOf(TimeSpan.FromMilliseconds(500))
                                       .RetryUntilSuccessful()
                                       .TimeoutAfter(TimeSpan.FromSeconds(1))
                                       .Invoke(() => 2 / zero);

            // Assert.
            Assert.Equal(0, result.Result);
            Assert.False(result.Successful);
            Assert.Equal(2, result.RetryInfo.Executions);
            Assert.IsType <DivideByZeroException>(result.RetryInfo.Exceptions.FirstOrDefault());
        }
Пример #6
0
    /// <summary>
    /// Processes an action with this retry policy.
    /// </summary>
    /// <param name="policy">The retry policy.</param>
    /// <param name="action">The async action which will return a result to process.</param>
    /// <param name="needThrow">A handler to check if need throw the exception without retry.</param>
    /// <param name="cancellationToken">The optional cancellation token.</param>
    /// <returns>The processing retry result.</returns>
    public static async Task <RetryResult <T> > ProcessAsync <T>(this IRetryPolicy policy, Func <CancellationToken, Task <T> > action, Func <Exception, Exception> needThrow, CancellationToken cancellationToken = default)
    {
        var result = new RetryResult <T>();

        if (action == null)
        {
            return(result);
        }
        if (needThrow == null)
        {
            needThrow = ex => ex;
        }
        var retry = policy?.CreateInstance() ?? new InternalRetryInstance();

        while (true)
        {
            try
            {
                cancellationToken.ThrowIfCancellationRequested();
                var r = await action(cancellationToken);

                result.Success(r);
                return(result);
            }
            catch (Exception ex)
            {
                result.Fail(ex);
                ex = needThrow(ex);
                if (ex != null)
                {
                    throw ex;
                }
            }

            var span = retry.Next();
            if (!span.HasValue)
            {
                result.End();
                return(result);
            }

            await Task.Delay(span.Value, cancellationToken);
        }
    }
Пример #7
0
 private void AssertHasException <T>(RetryResult <T> retryResult)
 {
     Assert.That(retryResult.HadException, Is.True);
     Assert.That(retryResult.LastException, Is.Not.Null);
 }
Пример #8
0
 private void AssertTimedOut <T>(RetryResult <T> retryResult)
 {
     Assert.That(retryResult.Success, Is.False);
     Assert.That(retryResult.TimedOut, Is.True);
 }
Пример #9
0
    /// <summary>
    /// Enables retry policy to process.
    /// </summary>
    /// <param name="cancellationToken">The optional cancellation token.</param>
    /// <returns>The processing retry result.</returns>
    public async Task <RetryResult> ProcessAsync(CancellationToken cancellationToken = default)
    {
        State = TaskStates.Initializing;
        var result = new RetryResult();
        var retry  = RetryPolicy?.CreateInstance() ?? new InternalRetryInstance();

        State = TaskStates.Working;
        while (true)
        {
            try
            {
                cancellationToken.ThrowIfCancellationRequested();
            }
            catch (OperationCanceledException)
            {
                State = TaskStates.Canceled;
                throw;
            }
            catch (ObjectDisposedException)
            {
                State = TaskStates.Canceled;
                throw;
            }

            try
            {
                await OnProcessAsync(cancellationToken);

                Processing?.Invoke(this, new RetryEventArgs(retry.ProcessTime));
                State = TaskStates.Done;
                result.Success();
                return(result);
            }
            catch (Exception ex)
            {
                State = TaskStates.WaitingToRetry;
                result.Fail(ex);
                try
                {
                    ex = ExceptionHandler.GetException(ex);
                }
                catch (Exception)
                {
                    State = TaskStates.Faulted;
                    throw;
                }

                if (ex != null)
                {
                    State = TaskStates.Faulted;
                    throw ex;
                }
            }

            var span = retry.Next();
            if (!span.HasValue)
            {
                State = TaskStates.Faulted;
                result.End();
                return(result);
            }

            await Task.Delay(span.Value, cancellationToken);

            State = TaskStates.Retrying;
        }
    }