Exemplo n.º 1
0
            public SemaphoreBulkheadHolder(GroupKey key, IMetricEvents metricEvents, MjolnirConfiguration config, IMjolnirLogFactory logFactory)
            {
                _key          = key;
                _metricEvents = metricEvents ?? throw new ArgumentNullException(nameof(metricEvents));
                _config       = config ?? throw new ArgumentNullException(nameof(config));

                if (logFactory == null)
                {
                    throw new ArgumentNullException(nameof(logFactory));
                }

                _log = logFactory.CreateLog <SemaphoreBulkheadHolder>();
                if (_log == null)
                {
                    throw new InvalidOperationException($"{nameof(IMjolnirLogFactory)} implementation returned null from {nameof(IMjolnirLogFactory.CreateLog)} for type {typeof(SemaphoreBulkheadHolder)}, please make sure the implementation returns a non-null log for all calls to {nameof(IMjolnirLogFactory.CreateLog)}");
                }

                // The order of things here is very intentional.
                // We get the MaxConcurrent value first and then initialize the semaphore bulkhead.
                // The change handler is registered after that. The order ought to help avoid a
                // situation where we might fire a config change handler before we add the
                // semaphore to the dictionary, potentially trying to add two entries with
                // different values in rapid succession.

                var value = _config.GetBulkheadConfiguration(key.Name).MaxConcurrent;

                _bulkhead = new SemaphoreBulkhead(_key, value);

                // On change, we'll replace the bulkhead. The assumption here is that a caller
                // using the bulkhead will have kept a local reference to the bulkhead that they
                // acquired a lock on, and will release the lock on that bulkhead and not one that
                // has been replaced after a config change.
                _config.OnConfigurationChanged(c => c.GetBulkheadConfiguration(key.Name).MaxConcurrent, UpdateMaxConcurrent);
            }
Exemplo n.º 2
0
 public CommandInvoker(
     MjolnirConfiguration config = null,
     IMjolnirLogFactory logFactory = null,
     IMetricEvents metricEvents = null,
     IBreakerExceptionHandler breakerExceptionHandler = null)
     : this(config, logFactory, metricEvents, breakerExceptionHandler, null)
 { }
Exemplo n.º 3
0
 public BulkheadInvoker(IBreakerInvoker breakerInvoker, IBulkheadFactory bulkheadFactory, IMetricEvents metricEvents, MjolnirConfiguration config)
 {
     _breakerInvoker  = breakerInvoker ?? throw new ArgumentNullException(nameof(breakerInvoker));
     _bulkheadFactory = bulkheadFactory ?? throw new ArgumentNullException(nameof(bulkheadFactory));
     _metricEvents    = metricEvents ?? throw new ArgumentNullException(nameof(metricEvents));
     _config          = config ?? throw new ArgumentNullException(nameof(config));
 }
Exemplo n.º 4
0
        public CircuitBreakerFactory(IMetricEvents metricEvents, IFailurePercentageCircuitBreakerConfig breakerConfig, IMjolnirLogFactory logFactory)
        {
            _metricEvents  = metricEvents ?? throw new ArgumentNullException(nameof(metricEvents));
            _breakerConfig = breakerConfig ?? throw new ArgumentNullException(nameof(breakerConfig));
            _logFactory    = logFactory ?? throw new ArgumentNullException(nameof(logFactory));

            _log = logFactory.CreateLog <FailurePercentageCircuitBreaker>();
            if (_log == null)
            {
                throw new InvalidOperationException($"{nameof(IMjolnirLogFactory)} implementation returned null from {nameof(IMjolnirLogFactory.CreateLog)} for type {typeof(CircuitBreakerFactory)}, please make sure the implementation returns a non-null log for all calls to {nameof(IMjolnirLogFactory.CreateLog)}");
            }

            _timer = new GaugeTimer(state =>
            {
                try
                {
                    var keys = _circuitBreakers.Keys;
                    foreach (var key in keys)
                    {
                        if (_circuitBreakers.TryGetValue(key, out Lazy <FailurePercentageCircuitBreaker> lazy) && lazy.IsValueCreated)
                        {
                            var breaker = lazy.Value;
                            _metricEvents.BreakerGauge(
                                breaker.Name,
                                _breakerConfig.GetMinimumOperations(key),
                                _breakerConfig.GetWindowMillis(key),
                                _breakerConfig.GetThresholdPercentage(key),
                                _breakerConfig.GetTrippedDurationMillis(key),
                                _breakerConfig.GetForceTripped(key),
                                _breakerConfig.GetForceFixed(key),
                                breaker.IsTripped(),
                                breaker.Metrics.SuccessCount,
                                breaker.Metrics.FailureCount);
                        }
                    }
                }
                catch (Exception e)
                {
                    _log.Error($"Error sending {nameof(IMetricEvents.BreakerGauge)} metric event", e);
                }
            });
        }
        internal FailurePercentageCircuitBreaker(GroupKey key, IClock clock, ICommandMetrics metrics, IStats stats, IMetricEvents metricEvents, FailurePercentageCircuitBreakerProperties properties, IConfigurableValue<long> gaugeIntervalMillisOverride = null)
        {
            _key = key;
            _clock = clock;
            _metrics = metrics;

            if (stats == null)
            {
                throw new ArgumentNullException("stats");
            }

            if (metricEvents == null)
            {
                throw new ArgumentNullException("metricEvents");
            }

            _stats = stats;
            _metricEvents = metricEvents;

            Properties = properties;
            _state = State.Fixed; // Start off assuming everything's fixed.
            _lastTrippedTimestamp = 0; // 0 is fine since it'll be far less than the first compared value.

            // Old gauge, will be phased out in v3.0 when IStats are removed.
            _statsTimer = new GaugeTimer((source, args) =>
            {
                var snapshot = _metrics.GetSnapshot();
                _stats.Gauge(StatsPrefix + " total", snapshot.Total >= properties.MinimumOperations.Value ? "Above" : "Below", snapshot.Total);
                _stats.Gauge(StatsPrefix + " error", snapshot.ErrorPercentage >= properties.ThresholdPercentage.Value ? "Above" : "Below", snapshot.ErrorPercentage);
            }, gaugeIntervalMillisOverride);

            _metricsTimer = new GaugeTimer((source, args) =>
            {
                _metricEvents.BreakerConfigGauge(
                    Name,
                    Properties.MinimumOperations.Value,
                    Properties.ThresholdPercentage.Value,
                    Properties.TrippedDurationMillis.Value);
            }, ConfigGaugeIntervalMillis);
        }
Exemplo n.º 6
0
        internal FailurePercentageCircuitBreaker(GroupKey key, IClock clock, ICommandMetrics metrics, IMetricEvents metricEvents, IFailurePercentageCircuitBreakerConfig config, IMjolnirLogFactory logFactory)
        {
            _metrics      = metrics ?? throw new ArgumentNullException(nameof(metrics));
            _clock        = clock ?? throw new ArgumentNullException(nameof(config));
            _config       = config ?? throw new ArgumentNullException(nameof(config));
            _metricEvents = metricEvents ?? throw new ArgumentNullException(nameof(metricEvents));

            if (logFactory == null)
            {
                throw new ArgumentNullException(nameof(logFactory));
            }

            _key = key;

            _log = logFactory.CreateLog <FailurePercentageCircuitBreaker>();
            if (_log == null)
            {
                throw new InvalidOperationException($"{nameof(IMjolnirLogFactory)} implementation returned null from {nameof(IMjolnirLogFactory.CreateLog)} for type {typeof(FailurePercentageCircuitBreaker)}, please make sure the implementation returns a non-null log for all calls to {nameof(IMjolnirLogFactory.CreateLog)}");
            }

            _state = State.Fixed;      // Start off assuming everything's fixed.
            _lastTrippedTimestamp = 0; // 0 is fine since it'll be far less than the first compared value.
        }
Exemplo n.º 7
0
        // ReSharper restore PrivateFieldCanBeConvertedToLocalVariable

        public BulkheadFactory(IMetricEvents metricEvents, MjolnirConfiguration config, IMjolnirLogFactory logFactory)
        {
            // No null checks on parameters; we don't use them, we're just passing them through to
            // the objects we're creating.

            _metricEvents = metricEvents;
            _config       = config;
            _logFactory   = logFactory ?? throw new ArgumentNullException(nameof(logFactory));

            var log = logFactory.CreateLog <BulkheadFactory>();

            if (log == null)
            {
                throw new InvalidOperationException($"{nameof(IMjolnirLogFactory)} implementation returned null from {nameof(IMjolnirLogFactory.CreateLog)} for type {typeof(BulkheadFactory)}, please make sure the implementation returns a non-null log for all calls to {nameof(IMjolnirLogFactory.CreateLog)}");
            }

            _timer = new GaugeTimer(state =>
            {
                try
                {
                    var keys = _bulkheads.Keys;
                    foreach (var key in keys)
                    {
                        if (_bulkheads.TryGetValue(key, out Lazy <SemaphoreBulkheadHolder> holder) && holder.IsValueCreated)
                        {
                            var bulkhead = holder.Value.Bulkhead;
                            _metricEvents.BulkheadGauge(bulkhead.Name, "semaphore", _config.GetBulkheadConfiguration(key.Name).MaxConcurrent, bulkhead.CountAvailable);
                        }
                    }
                }
                catch (Exception e)
                {
                    log.Error($"Error sending {nameof(IMetricEvents.BulkheadGauge)} metric event", e);
                }
            });
        }
Exemplo n.º 8
0
        // Internal constructor with a few extra arguments used by tests to inject mocks.
        internal CommandInvoker(
            MjolnirConfiguration config = null,
            IMjolnirLogFactory logFactory = null,
            IMetricEvents metricEvents = null,
            IBreakerExceptionHandler breakerExceptionHandler = null,
            IBulkheadInvoker bulkheadInvoker = null)
        {
            _config = config ?? new MjolnirConfiguration
            {
                IsEnabled = true,
                UseCircuitBreakers = false,
                IgnoreTimeouts = false
            };
            _logFactory = logFactory ?? new DefaultMjolnirLogFactory();
            _log = _logFactory.CreateLog<CommandInvoker>();
            if (_log == null)
            {
                throw new InvalidOperationException($"{nameof(IMjolnirLogFactory)} implementation returned null from {nameof(IMjolnirLogFactory.CreateLog)} for name {nameof(CommandInvoker)}, please make sure the implementation returns a non-null log for all calls to {nameof(IMjolnirLogFactory.CreateLog)}");
            }

            _metricEvents = metricEvents ?? new IgnoringMetricEvents();
            _breakerExceptionHandler = breakerExceptionHandler ?? new IgnoredExceptionHandler(new HashSet<Type>());

            _circuitBreakerFactory = new CircuitBreakerFactory(
                _metricEvents,
                new FailurePercentageCircuitBreakerConfig(_config),
                _logFactory);

            _bulkheadFactory = new BulkheadFactory(
                _metricEvents,
                _config,
                _logFactory);

            var breakerInvoker = new BreakerInvoker(_circuitBreakerFactory, _metricEvents, _breakerExceptionHandler);
            _bulkheadInvoker = bulkheadInvoker ?? new BulkheadInvoker(breakerInvoker, _bulkheadFactory, _metricEvents, _config);
        }
 public BreakerBuilder WithMetricEvents(IMetricEvents metricEvents)
 {
     _metricEvents = metricEvents;
     return(this);
 }
Exemplo n.º 10
0
 internal FailurePercentageCircuitBreaker(GroupKey key, ICommandMetrics metrics, IMetricEvents metricEvents, IFailurePercentageCircuitBreakerConfig config, IMjolnirLogFactory logFactory)
     : this(key, new UtcSystemClock(), metrics, metricEvents, config, logFactory)
 {
 }
 public BreakerBuilder WithMetricEvents(IMetricEvents metricEvents)
 {
     _metricEvents = metricEvents;
     return this;
 }
Exemplo n.º 12
0
 internal FailurePercentageCircuitBreaker(GroupKey key, ICommandMetrics metrics, IStats stats, IMetricEvents metricEvents, FailurePercentageCircuitBreakerProperties properties)
     : this(key, new SystemClock(), metrics, stats, metricEvents, properties) {}
Exemplo n.º 13
0
 public BreakerInvoker(ICircuitBreakerFactory circuitBreakerFactory, IMetricEvents metricEvents, IBreakerExceptionHandler ignoredExceptions)
 {
     _circuitBreakerFactory = circuitBreakerFactory ?? throw new ArgumentNullException(nameof(circuitBreakerFactory));
     _metricEvents          = metricEvents ?? throw new ArgumentNullException(nameof(metricEvents));
     _ignoredExceptions     = ignoredExceptions ?? throw new ArgumentNullException(nameof(ignoredExceptions));
 }
Exemplo n.º 14
0
            public SemaphoreBulkheadHolder(GroupKey key, IMetricEvents metricEvents)
            {
                if (metricEvents == null)
                {
                    throw new ArgumentNullException("metricEvents");
                }

                _metricEvents = metricEvents;

                // The order of things here is very intentional.
                // We create the configurable value first, retrieve its current value, and then
                // initialize the semaphore bulkhead. We register the change handler after that.
                // That ought to help avoid a situation where we might fire a config change handler
                // before we add the semaphore to the dictionary, potentially trying to add two
                // entries with different values in rapid succession.
                
                var configKey = "mjolnir.bulkhead." + key + ".maxConcurrent";
                _config = new ConfigurableValue<int>(configKey, DefaultBulkheadMaxConcurrent);

                var value = _config.Value;
                _bulkhead = new SemaphoreBulkhead(key, value);

                // On change, we'll replace the bulkhead. The assumption here is that a caller
                // using the bulkhead will have kept a local reference to the bulkhead that they
                // acquired a lock on, and will release the lock on that bulkhead and not one that
                // has been replaced after a config change.
                _config.AddChangeHandler(newLimit =>
                {
                    if (newLimit < 0)
                    {
                        Log.ErrorFormat("Semaphore bulkhead config {0} changed to an invalid limit of {0}, the bulkhead will not be changed",
                            configKey,
                            newLimit);
                        return;
                    }
                    
                    _bulkhead = new SemaphoreBulkhead(key, newLimit);
                });

                _timer = new GaugeTimer((source, args) =>
                {
                    _metricEvents.BulkheadConfigGauge(_bulkhead.Name, "semaphore", _config.Value);
                }, ConfigGaugeIntervalMillis);
            }