Пример #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ExecuteDynamicsServiceCall"/> class.
 /// </summary>
 /// <param name="logger">The logger<see cref="ILogger{ExecuteDynamicsServiceCall}"/></param>
 /// <param name="odataService">The odataService<see cref="IOdataService"/></param>
 public ExecuteDynamicsServiceCall(ILogger <ExecuteDynamicsServiceCall> logger, IOdataService odataService)
 {
     this.logger                          = logger;
     this.odataService                    = odataService;
     circuitBreakerStateCollection        = new ConcurrentDictionary <string, CircuitBreakerState>();
     defaultCircuitBreakerStateCollection = new CircuitBreakerState
     {
         ExceptionsCounter      = 0,
         ODataExceptionsCounter = 0,
         IsClosed = true,
         LastExceptionOccuredTime            = DateTime.MinValue.Ticks,
         TimespanDifferenceBetweenExceptions = CircuitBreakerPatternConstants.InitialTimespanDifference
     };
 }
Пример #2
0
        /// <summary>
        /// The ExecuteServiceCall
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func">The func<see cref="Func{Task{T}}"/></param>
        /// <param name="serviceName">The serviceName<see cref="string"/></param>
        /// <returns>The <see cref="Task{T}"/></returns>
        public async Task <T> ExecuteServiceCall <T>(Func <Task <T> > func, string serviceName = null)
        {
            try
            {
                if (serviceName == null)
                {
                    return(await this.TryWebCallWithRetry(func));
                }
                else
                {
                    CircuitBreakerState circuitBreakerState;
                    this.circuitBreakerStateCollection.TryGetValue(serviceName, out circuitBreakerState);
                    if (circuitBreakerState == null)
                    {
                        circuitBreakerStateCollection.TryAdd(serviceName, defaultCircuitBreakerStateCollection);
                        this.circuitBreakerStateCollection.TryGetValue(serviceName, out circuitBreakerState);
                    }

                    CircuitBreakerState newCircuitBreakerState = new CircuitBreakerState
                    {
                        ExceptionsCounter      = circuitBreakerState.ExceptionsCounter,
                        ODataExceptionsCounter = circuitBreakerState.ODataExceptionsCounter,
                        IsClosed = circuitBreakerState.IsClosed,
                        LastExceptionOccuredTime            = circuitBreakerState.LastExceptionOccuredTime,
                        TimespanDifferenceBetweenExceptions = circuitBreakerState.TimespanDifferenceBetweenExceptions
                    };

                    if (newCircuitBreakerState.IsClosed)
                    {
                        try
                        {
                            return(await this.TryWebCallWithRetry(func));
                        }
                        catch (Microsoft.OData.ODataException oDataException)
                        {
                            this.logger.LogError(oDataException, $"{serviceName} has thrown an OData Exception");

                            newCircuitBreakerState.ODataExceptionsCounter += 1;
                            this.UpdateCircuitBreakerState(newCircuitBreakerState);

                            ///TODO - In case we want to clear metadata cache after 4 failures for ANY api, we need to refactor the below dictionary entry to separately save a metadata cache exception state.
                            if (newCircuitBreakerState.TimespanDifferenceBetweenExceptions > CircuitBreakerPatternConstants.MaximumTimeDifferenceToResetCircuitBreaker)
                            {
                                newCircuitBreakerState.ODataExceptionsCounter = 0;
                                this.circuitBreakerStateCollection.TryUpdate(serviceName, newCircuitBreakerState, circuitBreakerState);
                                throw;
                            }
                            else if (newCircuitBreakerState.ODataExceptionsCounter > CircuitBreakerPatternConstants.MinimumExceptionCounterToOpenCircuit)
                            {
                                this.logger.LogError(oDataException, $"{serviceName} {ErrorMessages.MethodInOpenState}");
                                this.odataService.ResetOdataCache();
                                newCircuitBreakerState.IsClosed = false;
                            }
                            else
                            {
                                this.circuitBreakerStateCollection.TryUpdate(serviceName, newCircuitBreakerState, circuitBreakerState);
                                throw;
                            }
                        }
                        catch (Exception ex)
                        {
                            this.logger.LogError(ex, $"{serviceName} {ErrorMessages.MethodThrewAnException}", ex.Message);

                            newCircuitBreakerState.ExceptionsCounter += 1;
                            this.UpdateCircuitBreakerState(newCircuitBreakerState);

                            if (newCircuitBreakerState.TimespanDifferenceBetweenExceptions > CircuitBreakerPatternConstants.MaximumTimeDifferenceToResetCircuitBreaker)
                            {
                                newCircuitBreakerState.ExceptionsCounter = 0;
                                this.circuitBreakerStateCollection.TryUpdate(serviceName, newCircuitBreakerState, circuitBreakerState);
                                throw;
                            }
                            else if (newCircuitBreakerState.ExceptionsCounter > CircuitBreakerPatternConstants.MinimumExceptionCounterToOpenCircuit)
                            {
                                this.logger.LogError(ex, $"{serviceName} {ErrorMessages.MethodInOpenState}");

                                newCircuitBreakerState.IsClosed = false;
                            }
                            else
                            {
                                this.circuitBreakerStateCollection.TryUpdate(serviceName, newCircuitBreakerState, circuitBreakerState);
                                throw;
                            }
                        }
                    }
                    else
                    {
                        if (DateTime.UtcNow.Ticks - newCircuitBreakerState.LastExceptionOccuredTime > CircuitBreakerPatternConstants.MinimumTimeToTryClosingCircuit)
                        {
                            this.logger.LogInformation($"{serviceName} {ErrorMessages.MethodInHalfOpenState}");

                            try
                            {
                                var result = await this.TryWebCallWithRetry(func, 1);

                                newCircuitBreakerState.ExceptionsCounter      = 0;
                                newCircuitBreakerState.ODataExceptionsCounter = 0;
                                newCircuitBreakerState.IsClosed = true;

                                this.circuitBreakerStateCollection.TryUpdate(serviceName, newCircuitBreakerState, circuitBreakerState);

                                this.logger.LogInformation($"{serviceName} {ErrorMessages.MethodReturnToClosedState}");

                                return(result);
                            }
                            catch (Exception ex)
                            {
                                this.logger.LogError(ex, $"{serviceName} {ErrorMessages.MethodReturnToOpenState}", ex.Message);

                                newCircuitBreakerState.ExceptionsCounter      += 1;
                                newCircuitBreakerState.ODataExceptionsCounter += 1;
                                newCircuitBreakerState.TimespanDifferenceBetweenExceptions = DateTime.UtcNow.Ticks - newCircuitBreakerState.LastExceptionOccuredTime;
                                newCircuitBreakerState.LastExceptionOccuredTime            = DateTime.UtcNow.Ticks;
                            }
                        }
                        else
                        {
                            this.logger.LogError($"{serviceName} {ErrorMessages.MethodRunningInOpenState}");
                        }
                    }
                    this.circuitBreakerStateCollection.TryUpdate(serviceName, newCircuitBreakerState, circuitBreakerState);
                }
            }
            catch (InvalidOperationException ex) //For catching dictionary exceptions in case of threading issues (concurrent read/write operations)
            {
                this.logger.LogError(ex, $"{serviceName} {ErrorMessages.InvalidOperationException}", ex.Message);

                throw;
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, $"{serviceName} {ErrorMessages.GeneralException}", ex.Message);

                throw;
            }
            throw new CircuitBreakerException();
        }
Пример #3
0
 /// <summary>
 /// The UpdateCircuitBreakerState
 /// </summary>
 /// <param name="newCircuitBreakerState">The newCircuitBreakerState<see cref="CircuitBreakerState"/></param>
 private void UpdateCircuitBreakerState(CircuitBreakerState newCircuitBreakerState)
 {
     newCircuitBreakerState.TimespanDifferenceBetweenExceptions = DateTime.UtcNow.Ticks - newCircuitBreakerState.LastExceptionOccuredTime;
     newCircuitBreakerState.LastExceptionOccuredTime            = DateTime.UtcNow.Ticks;
 }