<commit_before>﻿using System;
using System.Diagnostics;
using System.Threading;
using Hudl.Common.Clock;
using Hudl.Config;
using Hudl.Mjolnir.External;
using Hudl.Mjolnir.Key;
using Hudl.Mjolnir.Metrics;
using Hudl.Mjolnir.Util;
using log4net;

namespace Hudl.Mjolnir.Breaker
{
    /// <summary>
    /// Trips when the error percentage rises above a configured threshold percentage.
    /// 
    /// This breaker is modeled after Hystrix's HystrixCircuitBreakerImpl. However, the metrics
    /// implementations that drive the breaker are fairly different.
    /// </summary>
    internal class FailurePercentageCircuitBreaker : ICircuitBreaker
    {
        internal readonly FailurePercentageCircuitBreakerProperties Properties;

        private static readonly ILog Log = LogManager.GetLogger(typeof (FailurePercentageCircuitBreaker));
        private static readonly IConfigurableValue<long> ConfigGaugeIntervalMillis = new ConfigurableValue<long>("mjolnir.breakerConfigGaugeIntervalMillis", 60000);

        private readonly object _stateChangeLock = new { };
        private readonly object _singleTestLock = new { };

        private readonly IClock _clock;
        private readonly ICommandMetrics _metrics;

        private readonly GroupKey _key;
        private readonly IStats _stats;
        private readonly IMetricEvents _metricEvents;

        // ReSharper disable NotAccessedField.Local
        // Don't let these get garbage collected.
        private readonly GaugeTimer _timer;
        private readonly GaugeTimer _timer2;
        // ReSharper restore NotAccessedField.Local
        
        private volatile State _state;
        private long _lastTrippedTimestamp;

        internal FailurePercentageCircuitBreaker(GroupKey key, ICommandMetrics metrics, IStats stats, IMetricEvents metricEvents, FailurePercentageCircuitBreakerProperties properties)
            : this(key, new SystemClock(), metrics, stats, metricEvents, properties) {}

        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.
            _timer = 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);

            _timer2 = new GaugeTimer((source, args) =>
            {
                _metricEvents.BreakerConfigGauge(
                    Name,
                    Properties.MinimumOperations.Value,
                    Properties.ThresholdPercentage.Value,
                    Properties.TrippedDurationMillis.Value);
            }, ConfigGaugeIntervalMillis);
        }

        public ICommandMetrics Metrics
        {
            get { return _metrics; }
        }

        private string StatsPrefix
        {
            get { return "mjolnir breaker " + _key; }
        }

        public string Name
        {
            get { return _key.Name; }<commit_msg>Rename timer variables for clarity<commit_after>﻿using System;
using System.Diagnostics;
using System.Threading;
using Hudl.Common.Clock;
using Hudl.Config;
using Hudl.Mjolnir.External;
using Hudl.Mjolnir.Key;
using Hudl.Mjolnir.Metrics;
using Hudl.Mjolnir.Util;
using log4net;

namespace Hudl.Mjolnir.Breaker
{
    /// <summary>
    /// Trips when the error percentage rises above a configured threshold percentage.
    /// 
    /// This breaker is modeled after Hystrix's HystrixCircuitBreakerImpl. However, the metrics
    /// implementations that drive the breaker are fairly different.
    /// </summary>
    internal class FailurePercentageCircuitBreaker : ICircuitBreaker
    {
        internal readonly FailurePercentageCircuitBreakerProperties Properties;

        private static readonly ILog Log = LogManager.GetLogger(typeof (FailurePercentageCircuitBreaker));
        private static readonly IConfigurableValue<long> ConfigGaugeIntervalMillis = new ConfigurableValue<long>("mjolnir.breakerConfigGaugeIntervalMillis", 60000);

        private readonly object _stateChangeLock = new { };
        private readonly object _singleTestLock = new { };

        private readonly IClock _clock;
        private readonly ICommandMetrics _metrics;

        private readonly GroupKey _key;
        private readonly IStats _stats;
        private readonly IMetricEvents _metricEvents;

        // Eventually the _statsTimer here will go away. IStats are deprecated and IMetrics are the
        // way of the future.
        // ReSharper disable NotAccessedField.Local
        // Don't let these get garbage collected.
        private readonly GaugeTimer _statsTimer;
        private readonly GaugeTimer _metricsTimer;
        // ReSharper restore NotAccessedField.Local
        
        private volatile State _state;
        private long _lastTrippedTimestamp;

        internal FailurePercentageCircuitBreaker(GroupKey key, ICommandMetrics metrics, IStats stats, IMetricEvents metricEvents, FailurePercentageCircuitBreakerProperties properties)
            : this(key, new SystemClock(), metrics, stats, metricEvents, properties) {}

        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);
        }

        public ICommandMetrics Metrics
        {
            get { return _metrics; }
        }

        private string StatsPrefix
        {
            get { return "mjolnir breaker " + _key; }
        }

        public string Name
        {
            get { return _key.Name; }
{'fraction_non_alphanumeric': 0.07076886170085163, 'fraction_numerical': 0.0026388389108792133, 'mean_word_length': 2.421419778416085, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1083057', 'n_tokens_mistral': 2129, 'n_tokens_neox': 2020, 'n_words': 576}