< Summary

Class:Microsoft.Azure.ServiceBus.RetryPolicy
Assembly:Microsoft.Azure.ServiceBus
File(s):C:\Git\azure-sdk-for-net\sdk\servicebus\Microsoft.Azure.ServiceBus\src\RetryPolicy.cs
Covered lines:64
Uncovered lines:6
Coverable lines:70
Total lines:197
Line coverage:91.4% (64 of 70)
Covered branches:35
Total branches:38
Branch coverage:92.1% (35 of 38)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-100%100%
.ctor()-100%100%
get_Default()-100%100%
get_NoRetry()-0%100%
get_IsServerBusy()-100%100%
get_ServerBusyExceptionMessage()-0%100%
RunOperation()-87.5%87.5%
IsRetryableException(...)-75%75%
ShouldRetry(...)-100%100%
SetServerBusy(...)-100%100%
ResetServerBusy()-100%100%
ScheduleResetServerBusy()-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\servicebus\Microsoft.Azure.ServiceBus\src\RetryPolicy.cs

#LineLine coverage
 1// Copyright (c) Microsoft. All rights reserved.
 2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 3
 4namespace Microsoft.Azure.ServiceBus
 5{
 6    using System;
 7    using System.Collections.Generic;
 8    using System.Threading.Tasks;
 9    using System.Transactions;
 10    using Primitives;
 11
 12    /// <summary>
 13    /// Represents an abstraction for retrying messaging operations. Users should not
 14    /// implement this class, and instead should use one of the provided implementations.
 15    /// <remarks>RetryPolicy will not be applied when an ambient transaction is found.</remarks>
 16    /// </summary>
 17    public abstract class RetryPolicy
 18    {
 219        internal static readonly TimeSpan ServerBusyBaseSleepTime = TimeSpan.FromSeconds(10);
 20
 21        const int DefaultRetryMaxCount = 5;
 222        static readonly TimeSpan DefaultRetryMinBackoff = TimeSpan.Zero;
 223        static readonly TimeSpan DefaultRetryMaxBackoff = TimeSpan.FromSeconds(30);
 24
 8425        readonly object serverBusyLock = new object();
 26
 27        // This is a volatile copy of IsServerBusy. IsServerBusy is synchronized with a lock, whereas encounteredServerB
 28        volatile bool encounteredServerBusy;
 29
 8430        protected RetryPolicy()
 31        {
 8432        }
 33
 34        /// <summary>
 35        /// Returns the default retry policy, <see cref="RetryExponential"/>.
 36        /// </summary>
 5637        public static RetryPolicy Default => new RetryExponential(DefaultRetryMinBackoff, DefaultRetryMaxBackoff, Defaul
 38
 39        /// <summary>
 40        /// Returns a <see cref="NoRetry"/> retry policy.
 41        /// </summary>
 042        public static RetryPolicy NoRetry => new NoRetry();
 43
 44        /// <summary>
 45        /// Determines whether or not the server returned a busy error.
 46        /// </summary>
 7047        public bool IsServerBusy { get; protected set; }
 48
 49        /// <summary>
 50        /// Gets the exception message when a server busy error is returned.
 51        /// </summary>
 052        public string ServerBusyExceptionMessage { get; protected set; }
 53
 54        /// <summary>
 55        /// Runs a <see cref="Func{T, TResult}"/>, using the current RetryPolicy.
 56        /// </summary>
 57        /// <param name="operation">A <see cref="Func{T, TResult}"/> to be executed.</param>
 58        /// <param name="operationTimeout">The timeout for the entire operation.</param>
 59        public async Task RunOperation(Func<Task> operation, TimeSpan operationTimeout)
 60        {
 861            var currentRetryCount = 0;
 862            List<Exception> exceptions = null;
 863            var timeoutHelper = new TimeoutHelper(operationTimeout, true);
 64
 865            if (this.IsServerBusy && timeoutHelper.RemainingTime() < RetryPolicy.ServerBusyBaseSleepTime)
 66            {
 67                // We are in a server busy state before we start processing.
 68                // Since ServerBusyBaseSleepTime > remaining time for the operation, we don't wait for the entire Sleep 
 069                await Task.Delay(timeoutHelper.RemainingTime()).ConfigureAwait(false);
 070                throw new ServerBusyException(this.ServerBusyExceptionMessage);
 71            }
 72
 73            while (true)
 74            {
 1275                if (this.IsServerBusy)
 76                {
 677                    await Task.Delay(RetryPolicy.ServerBusyBaseSleepTime).ConfigureAwait(false);
 78                }
 79
 80                try
 81                {
 1282                    await operation().ConfigureAwait(false);
 83
 84                    // Its a successful operation. Preemptively reset ServerBusy status.
 685                    this.ResetServerBusy();
 686                    break;
 87                }
 88                catch (Exception exception)
 89                {
 690                    currentRetryCount++;
 691                    if (exceptions == null)
 92                    {
 493                        exceptions = new List<Exception>();
 94                    }
 695                    exceptions.Add(exception);
 96
 697                    if (this.ShouldRetry(
 698                        timeoutHelper.RemainingTime(), currentRetryCount, exception, out var retryInterval)
 699                        && retryInterval < timeoutHelper.RemainingTime())
 100                    {
 101                        // Log intermediate exceptions.
 4102                        MessagingEventSource.Log.RunOperationExceptionEncountered(exception);
 4103                        await Task.Delay(retryInterval).ConfigureAwait(false);
 4104                        continue;
 105                    }
 106
 2107                    throw;
 0108                }
 109            }
 6110        }
 111
 112        /// <summary>
 113        /// Determines whether or not the exception can be retried.
 114        /// </summary>
 115        /// <returns>A bool indicating whether or not the operation can be retried.</returns>
 116        public virtual bool IsRetryableException(Exception exception)
 117        {
 92118            if (exception == null)
 119            {
 0120                throw Fx.Exception.ArgumentNull(nameof(exception));
 121            }
 122
 92123            var serviceBusException = exception as ServiceBusException;
 92124            return serviceBusException?.IsTransient == true;
 125        }
 126
 127        internal bool ShouldRetry(TimeSpan remainingTime, int currentRetryCount, Exception lastException, out TimeSpan r
 128        {
 129            // There is no exception information or there's there's an ambient transaction - should not retry
 94130            if (lastException == null || Transaction.Current != null)
 131            {
 2132                retryInterval = TimeSpan.Zero;
 2133                return false;
 134            }
 135
 92136            if (lastException is ServerBusyException)
 137            {
 16138                this.SetServerBusy(lastException.Message);
 139            }
 140
 92141            if (this.IsRetryableException(lastException))
 142            {
 72143                return this.OnShouldRetry(remainingTime, currentRetryCount, out retryInterval);
 144            }
 145
 20146            retryInterval = TimeSpan.Zero;
 20147            return false;
 148        }
 149
 150        internal void SetServerBusy(string exceptionMessage)
 151        {
 152            // multiple call to this method will not prolong the timer.
 18153            if (this.encounteredServerBusy)
 154            {
 2155                return;
 156            }
 157
 16158            lock (this.serverBusyLock)
 159            {
 16160                if (!this.encounteredServerBusy)
 161                {
 16162                    this.encounteredServerBusy = true;
 16163                    this.ServerBusyExceptionMessage = string.IsNullOrWhiteSpace(exceptionMessage) ?
 16164                        Resources.DefaultServerBusyException : exceptionMessage;
 16165                    this.IsServerBusy = true;
 16166                    TaskExtensionHelper.Schedule(ScheduleResetServerBusy);
 167                }
 16168            }
 16169        }
 170
 171        internal void ResetServerBusy()
 172        {
 22173            if (!this.encounteredServerBusy)
 174            {
 6175                return;
 176            }
 177
 16178            lock (this.serverBusyLock)
 179            {
 16180                if (this.encounteredServerBusy)
 181                {
 16182                    this.encounteredServerBusy = false;
 16183                    this.ServerBusyExceptionMessage = Resources.DefaultServerBusyException;
 16184                    this.IsServerBusy = false;
 185                }
 16186            }
 16187        }
 188
 189        protected abstract bool OnShouldRetry(TimeSpan remainingTime, int currentRetryCount, out TimeSpan retryInterval)
 190
 191        private async Task ScheduleResetServerBusy()
 192        {
 16193            await Task.Delay(RetryPolicy.ServerBusyBaseSleepTime).ConfigureAwait(false);
 16194            ResetServerBusy();
 16195        }
 196    }
 197}