Пример #1
0
        // @PublicExposureCandidate
        internal void ResetAggregation(DateTimeOffset periodStart)
        {
            periodStart = Util.RoundDownToSecond(periodStart);

            if (this.requiresPersistentAggregator)
            {
                IMetricSeriesAggregator aggregator = this.aggregatorPersistent;
                aggregator?.Reset(periodStart);
            }
            else
            {
                {
                    IMetricSeriesAggregator aggregator = UnwrapAggregator(this.aggregatorDefault);
                    aggregator?.Reset(periodStart);
                }

                {
                    IMetricSeriesAggregator aggregator = UnwrapAggregator(this.aggregatorQuickPulse);
                    aggregator?.Reset(periodStart);
                }

                {
                    IMetricSeriesAggregator aggregator = UnwrapAggregator(this.aggregatorCustom);
                    aggregator?.Reset(periodStart);
                }
            }
        }
Пример #2
0
        // @PublicExposureCandidate
        internal MetricAggregate GetCurrentAggregateUnsafe(MetricAggregationCycleKind aggregationCycleKind, DateTimeOffset dateTime)
        {
            IMetricSeriesAggregator aggregator = null;

            if (this.requiresPersistentAggregator)
            {
                aggregator = this.aggregatorPersistent;
            }
            else
            {
                switch (aggregationCycleKind)
                {
                case CycleKind.Default:
                    aggregator = UnwrapAggregator(this.aggregatorDefault);
                    break;

                case CycleKind.QuickPulse:
                    aggregator = UnwrapAggregator(this.aggregatorQuickPulse);
                    break;

                case CycleKind.Custom:
                    aggregator = UnwrapAggregator(this.aggregatorCustom);
                    break;

                default:
                    throw new ArgumentException(Invariant($"Unexpected value of {nameof(aggregationCycleKind)}: {aggregationCycleKind}."));
                }
            }

            dateTime = Util.RoundDownToSecond(dateTime);
            MetricAggregate aggregate = aggregator?.CreateAggregateUnsafe(dateTime);

            return(aggregate);
        }
        public static void GetDataSeries(IMetricSeriesAggregator aggregatorForConcreteSeries, IMetricSeriesAggregator aggregatorForNullSeries, MetricSeries series)
        {
            Assert.IsNotNull(aggregatorForConcreteSeries.DataSeries);
            Assert.AreSame(series, aggregatorForConcreteSeries.DataSeries);

            Assert.IsNull(aggregatorForNullSeries.DataSeries);
        }
        public static void TryRecycle_PersistentAggregator(IMetricSeriesAggregator accumulatorAggregator)
        {
            var startTS = new DateTimeOffset(2017, 9, 25, 17, 0, 0, TimeSpan.FromHours(-8));
            var endTS   = new DateTimeOffset(2017, 9, 25, 17, 1, 0, TimeSpan.FromHours(-8));

            long periodStringDef   = (long)(endTS - default(DateTimeOffset)).TotalMilliseconds;
            long periodStringStart = (long)(endTS - startTS).TotalMilliseconds;

            {
                accumulatorAggregator.TrackValue(10);

                MetricAggregate aggregate = accumulatorAggregator.CreateAggregateUnsafe(endTS);
                TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 10.0, max: 10.0, min: 10.0, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodStringDef, aggKindMoniker: "Microsoft.Azure.Accumulator");

                accumulatorAggregator.Reset(startTS, valueFilter: null);

                accumulatorAggregator.TrackValue(0);
                accumulatorAggregator.TrackValue(10);
                accumulatorAggregator.TrackValue(20);

                aggregate = accumulatorAggregator.CreateAggregateUnsafe(endTS);
                TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 30.0, max: 30.0, min: 0.0, stdDev: 0, timestamp: startTS, periodMs: periodStringStart, aggKindMoniker: "Microsoft.Azure.Accumulator");

                bool canRecycle = accumulatorAggregator.TryRecycle();

                Assert.IsFalse(canRecycle);

                aggregate = accumulatorAggregator.CreateAggregateUnsafe(endTS);
                TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "null", count: 2, sum: 30.0, max: 30.0, min: 0.0, stdDev: 5.0, timestamp: startTS, periodMs: periodStringStart, aggKindMoniker: "Microsoft.Azure.Accumulator");
            }
        }
Пример #5
0
        private IMetricSeriesAggregator GetOrCreatePersistentAggregator()
        {
            IMetricSeriesAggregator aggregator = this.aggregatorPersistent;

            if (aggregator == null)
            {
                aggregator = this.configuration.CreateNewAggregator(this, CycleKind.Default);
                IMetricSeriesAggregator prevAggregator = Interlocked.CompareExchange(ref this.aggregatorPersistent, aggregator, null);

                if (prevAggregator == null)
                {
                    bool added = this.aggregationManager.AddAggregator(aggregator, CycleKind.Default);
                    if (added == false)
                    {
                        throw new InvalidOperationException("Internal SDK gub. Please report!"
                                                            + " Info: _aggregationManager.AddAggregator reports false for a PERSISTENT aggregator."
                                                            + " This should never happen.");
                    }
                }
                else
                {
                    aggregator = prevAggregator;
                }
            }

            return(aggregator);
        }
        private IMetricSeriesAggregator GetOrCreatePersistentAggregator()
        {
            IMetricSeriesAggregator aggregator = _aggregatorPersistent;

            if (aggregator == null)
            {
                aggregator = _configuration.CreateNewAggregator(this, CycleKind.Default);
                IMetricSeriesAggregator prevAggregator = Interlocked.CompareExchange(ref _aggregatorPersistent, aggregator, null);

                if (prevAggregator == null)
                {
                    bool added = _aggregationManager.AddAggregator(aggregator, CycleKind.Default);
                    if (added == false)
                    {
                        return(GetOrCreatePersistentAggregator());
                    }
                }
                else
                {
                    aggregator = prevAggregator;
                }
            }

            return(aggregator);
        }
Пример #7
0
        internal void ClearAggregator(MetricAggregationCycleKind aggregationCycleKind)
        {
            if (this.requiresPersistentAggregator)
            {
                return;
            }

            WeakReference <IMetricSeriesAggregator> aggregatorWeakRef;

            switch (aggregationCycleKind)
            {
            case CycleKind.Default:
                aggregatorWeakRef = Interlocked.Exchange(ref this.aggregatorDefault, null);
                this.aggregatorRecycleCacheDefault = UnwrapAggregator(aggregatorWeakRef);
                break;

            case CycleKind.QuickPulse:
                aggregatorWeakRef = Interlocked.Exchange(ref this.aggregatorQuickPulse, null);
                this.aggregatorRecycleCacheQuickPulse = UnwrapAggregator(aggregatorWeakRef);
                break;

            case CycleKind.Custom:
                aggregatorWeakRef = Interlocked.Exchange(ref this.aggregatorCustom, null);
                this.aggregatorRecycleCacheCustom = UnwrapAggregator(aggregatorWeakRef);
                break;

            default:
                throw new ArgumentException(Invariant($"Unexpected value of {nameof(aggregationCycleKind)}: {aggregationCycleKind}."));
            }
        }
Пример #8
0
        internal MetricSeries(
            MetricAggregationManager aggregationManager,
            string metricId,
            IEnumerable <KeyValuePair <string, string> > dimensionNamesAndValues,
            IMetricSeriesConfiguration configuration)
        {
            Util.ValidateNotNull(aggregationManager, nameof(aggregationManager));
            Util.ValidateNotNull(metricId, nameof(metricId));
            Util.ValidateNotNull(configuration, nameof(configuration));

            _aggregationManager           = aggregationManager;
            _metricId                     = metricId;
            _configuration                = configuration;
            _requiresPersistentAggregator = configuration.RequiresPersistentAggregation;

            var dimNameVals = new Dictionary <string, string>();

            if (dimensionNamesAndValues != null)
            {
                int dimIndex = 0;
                foreach (KeyValuePair <string, string> dimNameVal in dimensionNamesAndValues)
                {
                    if (dimNameVal.Key == null)
                    {
                        throw new ArgumentNullException($"The name for dimension at 0-based index '{dimIndex}' is null.");
                    }

                    if (String.IsNullOrWhiteSpace(dimNameVal.Key))
                    {
                        throw new ArgumentException($"The name for dimension at 0-based index '{dimIndex}' is empty or white-space");
                    }

                    if (dimNameVal.Value == null)
                    {
                        throw new ArgumentNullException($"The value for dimension '{dimNameVal.Key}' number is null.");
                    }

                    if (String.IsNullOrWhiteSpace(dimNameVal.Value))
                    {
                        throw new ArgumentNullException($"The value for dimension '{dimNameVal.Key}' is empty or white-space.");
                    }

                    dimNameVals[dimNameVal.Key] = dimNameVal.Value;
                    dimIndex++;
                }
            }

            _dimensionNamesAndValues = dimNameVals;

            _aggregatorPersistent = null;
            _aggregatorDefault    = null;
            _aggregatorQuickPulse = null;
            _aggregatorCustom     = null;
        }
Пример #9
0
        private static IMetricSeriesAggregator UnwrapAggregator(WeakReference <IMetricSeriesAggregator> aggregatorWeakRef)
        {
            if (aggregatorWeakRef != null)
            {
                IMetricSeriesAggregator aggregatorHardRef = null;
                if (aggregatorWeakRef.TryGetTarget(out aggregatorHardRef))
                {
                    return(aggregatorHardRef);
                }
            }

            return(null);
        }
Пример #10
0
 private static void TrackValue(IMetricSeriesAggregator aggregator, object metricValue, ref List <Exception> errors)
 {
     if (aggregator != null)
     {
         try
         {
             aggregator.TrackValue(metricValue);
         }
         catch (Exception ex)
         {
             (errors = errors ?? new List <Exception>()).Add(ex);
         }
     }
 }
        private List <MetricAggregate> GetPersistentAggregations(DateTimeOffset tactTimestamp, IMetricSeriesFilter previousFilter)
        {
            // Complete each persistent aggregator:
            // The Enumerator of GrowingCollection is a thread-safe lock-free implementation that operates on a "snapshot" of a collection taken at the
            // time when the enumerator is created. We expand the foreach statement (like the compiler normally does) so that we can use the typed
            // enumerator's Count property which is constsent with the data in the snapshot.

            GrowingCollection <IMetricSeriesAggregator> .Enumerator persistentValsAggregators = _aggregatorsForPersistent.Aggregators.GetEnumerator();
            List <MetricAggregate> persistentValsAggregations = new List <MetricAggregate>(capacity: persistentValsAggregators.Count);

            try
            {
                while (persistentValsAggregators.MoveNext())
                {
                    IMetricSeriesAggregator aggregator = persistentValsAggregators.Current;
                    if (aggregator != null)
                    {
                        // Persistent aggregators are always active, regardless of filters for a particular cycle.
                        // But we can apply the cycle's filters to determine whether or not to pull the aggregator
                        // for a aggregate at this time. Of course, only series filters, not value filters, can be considered.
                        IMetricValueFilter unusedValueFilter;
                        bool satisfiesFilter = (previousFilter == null)
                                               ||
                                               (previousFilter.WillConsume(aggregator.DataSeries, out unusedValueFilter));
                        if (satisfiesFilter)
                        {
                            MetricAggregate aggregate = aggregator.CompleteAggregation(tactTimestamp);

                            if (aggregate != null)
                            {
                                persistentValsAggregations.Add(aggregate);
                            }
                        }
                    }
                }
            }
            finally
            {
                persistentValsAggregators.Dispose();
            }

            return(persistentValsAggregations);
        }
        private static bool AddAggregator(IMetricSeriesAggregator aggregator, AggregatorCollection aggregatorCollection)
        {
            if (aggregatorCollection == null)
            {
                return(false);
            }

            IMetricSeriesFilter seriesFilter = aggregatorCollection.Filter;
            IMetricValueFilter  valueFilter  = null;

            if (seriesFilter != null && !seriesFilter.WillConsume(aggregator.DataSeries, out valueFilter))
            {
                return(false);
            }

            aggregator.Reset(aggregatorCollection.PeriodStart, valueFilter);
            aggregatorCollection.Aggregators.Add(aggregator);

            return(true);
        }
        private static async Task TrackValueConcurrentWorker(IMetricSeriesAggregator aggregator)
        {
            await Task.Delay(3);

            int currentWorkersAtStart = Interlocked.Increment(ref s_trackValueConcurrentWorker_Current);

            try
            {
                int maxWorkers = Volatile.Read(ref s_trackValueConcurrentWorker_Max);
                while (currentWorkersAtStart > maxWorkers)
                {
                    int prevMaxWorkers = Interlocked.CompareExchange(ref s_trackValueConcurrentWorker_Max, currentWorkersAtStart, maxWorkers);

                    if (prevMaxWorkers == maxWorkers)
                    {
                        break;
                    }
                    else
                    {
                        maxWorkers = Volatile.Read(ref s_trackValueConcurrentWorker_Max);
                    }
                }

                for (int i = 0; i < 500; i++)
                {
                    for (int v = 0; v <= 300000; v += 3000)
                    {
                        aggregator.TrackValue(v);
                        //Thread.Yield();
                        await Task.Delay(0);
                    }

                    await Task.Delay(1);
                }
            }
            finally
            {
                Interlocked.Decrement(ref s_trackValueConcurrentWorker_Current);
            }
        }
        public static void CompleteAggregation_NonpersistentAggregator(IMetricSeriesAggregator measurementAggregator)
        {
            var  startTS      = new DateTimeOffset(2017, 9, 25, 17, 0, 0, TimeSpan.FromHours(-8));
            var  endTS        = new DateTimeOffset(2017, 9, 25, 17, 1, 0, TimeSpan.FromHours(-8));
            long periodString = (long)(endTS - startTS).TotalMilliseconds;

            int filterInvocationsCount = 0;

            measurementAggregator.Reset(startTS, valueFilter: new CustomDoubleValueFilter((s, v) => { filterInvocationsCount++; return(true); }));

            Assert.AreEqual(0, filterInvocationsCount);

            measurementAggregator.TrackValue(1);
            measurementAggregator.TrackValue("2");

            MetricAggregate aggregate = measurementAggregator.CompleteAggregation(endTS);

            TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "Cows Sold", count: 2, sum: 3, max: 2, min: 1, stdDev: 0.5, timestamp: startTS, periodMs: periodString, aggKindMoniker: "Microsoft.Azure.SimpleStatistics");
            Assert.AreEqual(2, filterInvocationsCount);

            measurementAggregator.TrackValue("3");
            measurementAggregator.TrackValue(4);

            aggregate = measurementAggregator.CompleteAggregation(endTS);
            //// In earlier versions we used to reject further values after Complete Aggregation was called. However, that complication is not really necesary.

            //TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "Cows Sold", count: 2, sum: 3, max: 2, min: 1, stdDev: 0.5, timestamp: startTS, periodMs: periodString, aggKindMoniker: "Microsoft.Azure.SimpleStatistics");
            //Assert.AreEqual(2, filterInvocationsCount);

            //aggregate = measurementAggregator.CreateAggregateUnsafe(endTS);
            //TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "Cows Sold", count: 2, sum: 3, max: 2, min: 1, stdDev: 0.5, timestamp: startTS, periodMs: periodString, aggKindMoniker: "Microsoft.Azure.SimpleStatistics");

            TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "Cows Sold", count: 4, sum: 10, max: 4, min: 1, stdDev: 1.11803398874989, timestamp: startTS, periodMs: periodString, aggKindMoniker: "Microsoft.Azure.SimpleStatistics");
            Assert.AreEqual(4, filterInvocationsCount);

            aggregate = measurementAggregator.CreateAggregateUnsafe(endTS);
            TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "Cows Sold", count: 4, sum: 10, max: 4, min: 1, stdDev: 1.11803398874989, timestamp: startTS, periodMs: periodString, aggKindMoniker: "Microsoft.Azure.SimpleStatistics");
        }
        public static void CreateAggregateUnsafe(
            IMetricSeriesAggregator aggregator,
            MetricSeries metric,
            IEnumerable <KeyValuePair <string, string> > expectedDimensionNamesValues)
        {
            var startTS = new DateTimeOffset(2017, 9, 25, 17, 0, 0, TimeSpan.FromHours(-8));
            var endTS   = new DateTimeOffset(2017, 9, 25, 17, 1, 0, TimeSpan.FromHours(-8));

            aggregator.Reset(startTS, valueFilter: null);

            aggregator.TrackValue(42);
            aggregator.TrackValue(43);

            MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);

            Assert.IsNotNull(aggregate);

            Assert.AreEqual("Cows Sold", aggregate.MetricId, "aggregate.MetricId mismatch");
            Assert.AreEqual(2, aggregate.AggregateData["Count"], "aggregate.AggregateData[Count] mismatch");
            Assert.AreEqual(85, (double)aggregate.AggregateData["Sum"], TestUtil.Util.MaxAllowedPrecisionError, "aggregate.AggregateData[Sum] mismatch");
            Assert.AreEqual(43, (double)aggregate.AggregateData["Max"], TestUtil.Util.MaxAllowedPrecisionError, "aggregate.AggregateData[Max] mismatch");
            Assert.AreEqual(42, (double)aggregate.AggregateData["Min"], TestUtil.Util.MaxAllowedPrecisionError, "aggregate.AggregateData[Min] mismatch");
            Assert.AreEqual(0.5, (double)aggregate.AggregateData["StdDev"], TestUtil.Util.MaxAllowedPrecisionError, "aggregate.AggregateData[StdDev] mismatch");

            Assert.AreEqual(startTS, aggregate.AggregationPeriodStart, "aggregate.AggregationPeriodStart mismatch");
            Assert.AreEqual(
                (endTS - startTS).TotalMilliseconds,
                aggregate.AggregationPeriodDuration.TotalMilliseconds,
                "aggregate.AggregationPeriodDuration mismatch");

            Assert.AreEqual(expectedDimensionNamesValues.Count(), aggregate.Dimensions.Count);

            foreach (KeyValuePair <string, string> dimNameValue in expectedDimensionNamesValues)
            {
                Assert.IsTrue(aggregate.Dimensions.ContainsKey(dimNameValue.Key), $"missing aggregate.Dimensions[{dimNameValue.Key}]");
                Assert.AreEqual(dimNameValue.Value, aggregate.Dimensions[dimNameValue.Key], $"wrong aggregate.Dimensions[{dimNameValue.Key}]");
            }
        }
        public static void CompleteAggregation_PersistentAggregator(IMetricSeriesAggregator accumulatorAggregator)
        {
            var  startTS      = new DateTimeOffset(2017, 9, 25, 17, 0, 0, TimeSpan.FromHours(-8));
            var  endTS        = new DateTimeOffset(2017, 9, 25, 17, 1, 0, TimeSpan.FromHours(-8));
            long periodString = (long)(endTS - startTS).TotalMilliseconds;

            int filterInvocationsCount = 0;

            accumulatorAggregator.Reset(startTS, valueFilter: new CustomDoubleValueFilter((s, v) => { filterInvocationsCount++; return(true); }));

            Assert.AreEqual(0, filterInvocationsCount);

            accumulatorAggregator.TrackValue(1);
            accumulatorAggregator.TrackValue("2");

            MetricAggregate aggregate = accumulatorAggregator.CompleteAggregation(endTS);

            TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "Cows Sold", count: 0, sum: 3, max: 3, min: 1, stdDev: 0, timestamp: startTS, periodMs: periodString, aggKindMoniker: "Microsoft.Azure.Accumulator");
            Assert.AreEqual(2, filterInvocationsCount);

            accumulatorAggregator.TrackValue("3");
            accumulatorAggregator.TrackValue(4);

            aggregate = accumulatorAggregator.CompleteAggregation(endTS);
            TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "Cows Sold", count: 0, sum: 10, max: 10, min: 1, stdDev: 0, timestamp: startTS, periodMs: periodString, aggKindMoniker: "Microsoft.Azure.Accumulator");
            Assert.AreEqual(4, filterInvocationsCount);

            aggregate = accumulatorAggregator.CreateAggregateUnsafe(endTS);
            TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "Cows Sold", count: 0, sum: 10, max: 10, min: 1, stdDev: 0, timestamp: startTS, periodMs: periodString, aggKindMoniker: "Microsoft.Azure.Accumulator");

            accumulatorAggregator.TrackValue(-18);
            accumulatorAggregator.TrackValue(2);

            aggregate = accumulatorAggregator.CompleteAggregation(endTS);
            TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "Cows Sold", count: 0, sum: -6, max: 10, min: -8, stdDev: 0, timestamp: startTS, periodMs: periodString, aggKindMoniker: "Microsoft.Azure.Accumulator");
            Assert.AreEqual(6, filterInvocationsCount);
        }
        private static bool AddAggregator(IMetricSeriesAggregator aggregator, AggregatorCollection aggregatorCollection)
        {
            if (aggregatorCollection == null)
            {
                return(false);
            }

            IMetricSeriesFilter seriesFilter = aggregatorCollection.Filter;
            IMetricValueFilter  valueFilter;

            if (false == Util.FilterWillConsume(seriesFilter, aggregator.DataSeries, out valueFilter))
            {
                return(false);
            }

            // @ToDo
            // @PublicExposureCandidate
            // aggregator.Reset(aggregatorCollection.PeriodStart, valueFilter);
            aggregator.Reset(aggregatorCollection.PeriodStart);

            aggregatorCollection.Aggregators.Add(aggregator);

            return(true);
        }
Пример #18
0
        /// <summary>
        /// The lifetime of an aggragator can easily be a minute or so. So, it is a relatively small object that can easily get into Gen-2 GC heap,
        /// but then will need to be reclaimed from there relatively quickly. This can lead to a fragmentation of Gen-2 heap. To avoid this we employ
        /// a simple form of object pooling: Each data series keeps an instance of a past aggregator and tries to reuse it.
        /// Aggregator implementations which believe that they are too expensive to recycle for this, can opt out of this strategy by returning FALSE from
        /// their CanRecycle property.
        /// </summary>
        /// <param name="aggregationCycleKind">The kind of the metric aggregation cycle.</param>
        /// <returns>An empty aggregator.</returns>
        private IMetricSeriesAggregator GetRecycledAggregatorInstance(MetricAggregationCycleKind aggregationCycleKind)
        {
            if (this.requiresPersistentAggregator)
            {
                return(null);
            }

            IMetricSeriesAggregator aggregator = null;

            switch (aggregationCycleKind)
            {
            case CycleKind.Default:
                aggregator = Interlocked.Exchange(ref this.aggregatorRecycleCacheDefault, null);
                break;

            case CycleKind.QuickPulse:
                aggregator = Interlocked.Exchange(ref this.aggregatorRecycleCacheQuickPulse, null);
                break;

            case CycleKind.Custom:
                aggregator = Interlocked.Exchange(ref this.aggregatorRecycleCacheCustom, null);
                break;
            }

            if (aggregator == null)
            {
                return(null);
            }

            if (aggregator.TryRecycle())
            {
                return(aggregator);
            }

            return(null);
        }
        internal bool AddAggregator(IMetricSeriesAggregator aggregator, MetricAggregationCycleKind aggregationCycleKind)
        {
            Util.ValidateNotNull(aggregator, nameof(aggregator));

            if (aggregator.DataSeries._configuration.RequiresPersistentAggregation)
            {
                return(AddAggregator(aggregator, _aggregatorsForPersistent));
            }

            switch (aggregationCycleKind)
            {
            case CycleKind.Default:
                return(AddAggregator(aggregator, _aggregatorsForDefault));

            case CycleKind.QuickPulse:
                return(AddAggregator(aggregator, _aggregatorsForQuickPulse));

            case CycleKind.Custom:
                return(AddAggregator(aggregator, _aggregatorsForCustom));

            default:
                throw new ArgumentException($"Unexpected value of {nameof(aggregationCycleKind)}: {aggregationCycleKind}.");
            }
        }
Пример #20
0
        private IMetricSeriesAggregator GetNewOrRecycledAggregatorInstance(MetricAggregationCycleKind aggregationCycleKind)
        {
            IMetricSeriesAggregator aggregator = this.GetRecycledAggregatorInstance(aggregationCycleKind);

            return(aggregator ?? this.configuration.CreateNewAggregator(this, aggregationCycleKind));
        }
Пример #21
0
        private IMetricSeriesAggregator GetOrCreateAggregator(MetricAggregationCycleKind aggregationCycleKind, ref WeakReference <IMetricSeriesAggregator> aggregatorWeakRef)
        {
            while (true)
            {
                // Local cache for the reference in case of concurrnet updates:
                WeakReference <IMetricSeriesAggregator> currentAggregatorWeakRef = aggregatorWeakRef;

                // Try to dereference the weak reference:
                IMetricSeriesAggregator aggregatorHardRef = UnwrapAggregator(currentAggregatorWeakRef);
                if (aggregatorHardRef != null)
                {
                    return(aggregatorHardRef);
                }

                // END OF FAST PATH. Could not dereference aggregator. Will attempt to create it...

                // So aggretator is NULL. For non-default cycle kinds, see if the there is even a cycle hooked up:

                if (aggregationCycleKind != CycleKind.Default)
                {
                    IMetricSeriesFilter dataSeriesFilter;
                    if (false == this.aggregationManager.IsCycleActive(aggregationCycleKind, out dataSeriesFilter))
                    {
                        return(null);
                    }

                    // Ok, a cycle is, indeed, acgive up. See if the cycle's filter is interested in this series:

                    // Respect the filter. Note: Filter may be user code. If user code is broken, assume we accept the series.
                    IMetricValueFilter valuesFilter;
                    if (false == Util.FilterWillConsume(dataSeriesFilter, this, out valuesFilter))
                    {
                        return(null);
                    }
                }

                // Ok, they want to consume us. Create new aggregator and a weak reference to it:

                IMetricSeriesAggregator newAggregator = this.GetNewOrRecycledAggregatorInstance(aggregationCycleKind);
                WeakReference <IMetricSeriesAggregator> newAggregatorWeakRef = new WeakReference <IMetricSeriesAggregator>(newAggregator, trackResurrection: false);

                // Store the weak reference to the aggregator. However, there is a race on doing it, so check for it:
                WeakReference <IMetricSeriesAggregator> prevAggregatorWeakRef = Interlocked.CompareExchange(ref aggregatorWeakRef, newAggregatorWeakRef, currentAggregatorWeakRef);
                if (prevAggregatorWeakRef == currentAggregatorWeakRef)
                {
                    // We won the race and stored the aggregator. Now tell the manager about it and go on using it.
                    // Note that the status of IsCycleActive and the dataSeriesFilter may have changed concurrently.
                    // So it is essential that we do this after the above interlocked assignment of aggregator.
                    // It ensures that only objects are added to the aggregator collection that are also referenced by the data series.
                    // In addition, AddAggregator(..) always uses the current value of the aggregator-collection in a thread-safe manner.
                    // Becasue the aggregator collection reference is always updated before telling the aggregators to cycle,
                    // we know that any aggregator added will be eventually cycled.
                    // If adding succeeds, AddAggregator(..) will have set the correct filter.
                    bool added = this.aggregationManager.AddAggregator(newAggregator, aggregationCycleKind);

                    // If the manager does not accept the addition, it means that the aggregationCycleKind is disabled or that the filter is not interested in this data series.
                    // Either way we need to give up.
                    if (added)
                    {
                        return(newAggregator);
                    }
                    else
                    {
                        // We could have accepted some values into this aggregator in the short time it was set in this series. We will loose those values.
                        Interlocked.CompareExchange(ref aggregatorWeakRef, null, newAggregatorWeakRef);
                        return(null);
                    }
                }

                // We lost the race and a different aggregator was used. Loop again. Doing so will attempt to dereference the latest aggregator reference.
            }
        }
Пример #22
0
        internal MetricSeries(
            MetricAggregationManager aggregationManager,
            MetricIdentifier metricIdentifier,
            IEnumerable <KeyValuePair <string, string> > dimensionNamesAndValues,
            IMetricSeriesConfiguration configuration)
        {
            // Validate and store aggregationManager:
            Util.ValidateNotNull(aggregationManager, nameof(aggregationManager));
            this.aggregationManager = aggregationManager;

            // Validate and store metricIdentifier:
            Util.ValidateNotNull(metricIdentifier, nameof(metricIdentifier));
            this.MetricIdentifier = metricIdentifier;

            // Copy dimensionNamesAndValues, validate values (keys are implicitly validated as they need to match the keys in the identifier):
            var dimNameVals = new Dictionary <string, string>();

            if (dimensionNamesAndValues != null)
            {
                int dimIndex = 0;
                foreach (KeyValuePair <string, string> dimNameVal in dimensionNamesAndValues)
                {
                    if (dimNameVal.Value == null)
                    {
                        throw new ArgumentNullException(Invariant($"The value for dimension '{dimNameVal.Key}' number is null."));
                    }

                    if (String.IsNullOrWhiteSpace(dimNameVal.Value))
                    {
                        throw new ArgumentNullException(Invariant($"The value for dimension '{dimNameVal.Key}' is empty or white-space."));
                    }

                    dimNameVals[dimNameVal.Key] = dimNameVal.Value;
                    dimIndex++;
                }
            }

            // Validate that metricIdentifier and dimensionNamesAndValues contain consistent dimension names:
            if (metricIdentifier.DimensionsCount != dimNameVals.Count)
            {
                throw new ArgumentException(Invariant($"The specified {nameof(metricIdentifier)} contains {metricIdentifier.DimensionsCount} dimensions,")
                                            + Invariant($" however the specified {nameof(dimensionNamesAndValues)} contains {dimNameVals.Count} name-value pairs with unique names."));
            }

            foreach (string dimName in metricIdentifier.GetDimensionNames())
            {
                if (false == dimNameVals.ContainsKey(dimName))
                {
                    throw new ArgumentException(Invariant($"The specified {nameof(metricIdentifier)} contains a dimension named \"{dimName}\",")
                                                + Invariant($" however the specified {nameof(dimensionNamesAndValues)} does not contain an entry for that name."));
                }
            }

            // Store copied dimensionNamesAndValues:
            this.dimensionNamesAndValues = dimNameVals;

            // Validate and store configuration:
            Util.ValidateNotNull(configuration, nameof(configuration));
            this.configuration = configuration;
            this.requiresPersistentAggregator = configuration.RequiresPersistentAggregation;

            // Init other instance vars:
            this.aggregatorPersistent = null;
            this.aggregatorDefault    = null;
            this.aggregatorQuickPulse = null;
            this.aggregatorCustom     = null;
        }
        public static void Reset_PersistentAggregator(IMetricSeriesAggregator aggregator, string aggregateKindMoniker)
        {
            Assert.AreEqual(MetricAggregateKinds.Accumulator.Moniker, aggregateKindMoniker);

            var startTS = new DateTimeOffset(2017, 9, 25, 17, 0, 0, TimeSpan.FromHours(-8));
            var endTS   = new DateTimeOffset(2017, 9, 25, 17, 1, 0, TimeSpan.FromHours(-8));

            long periodStringDef   = (long)(endTS - default(DateTimeOffset)).TotalMilliseconds;
            long periodStringStart = (long)(endTS - startTS).TotalMilliseconds;

            int filterInvocationsCount = 0;

            MetricAggregate aggregate = aggregator.CreateAggregateUnsafe(endTS);

            Assert.IsNull(aggregate);

            aggregator.Reset(startTS, valueFilter: null);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            Assert.IsNull(aggregate);

            aggregator.TrackValue(10);
            aggregator.TrackValue(20);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 30.0, max: 30, min: 10.0, stdDev: 5.0, timestamp: startTS, periodMs: periodStringStart, aggKindMoniker: aggregateKindMoniker);
            Assert.AreEqual(0, filterInvocationsCount);

            aggregator.Reset(default(DateTimeOffset), valueFilter: new CustomDoubleValueFilter((s, v) => { filterInvocationsCount++; return(true); }));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            Assert.IsNull(aggregate);

            aggregator.TrackValue(100);
            aggregator.TrackValue(200);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: 300, max: 300, min: 100, stdDev: 0, timestamp: default(DateTimeOffset), periodMs: periodStringDef, aggKindMoniker: aggregateKindMoniker);
            Assert.AreEqual(2, filterInvocationsCount);

            aggregator.Reset(startTS, valueFilter: new CustomDoubleValueFilter((s, v) => { filterInvocationsCount++; return(false); }));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            Assert.IsNull(aggregate);

            aggregator.TrackValue(100);
            aggregator.TrackValue(200);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            Assert.IsNull(aggregate);
            Assert.AreEqual(4, filterInvocationsCount);

            aggregator.Reset(startTS, valueFilter: new CustomDoubleValueFilter((s, v) => { filterInvocationsCount++; return(true); }));

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            Assert.IsNull(aggregate);

            aggregator.TrackValue(100);
            aggregator.TrackValue(-200);

            aggregate = aggregator.CreateAggregateUnsafe(endTS);
            TestUtil.Util.ValidateNumericAggregateValues(aggregate, name: "null", count: 0, sum: -100, max: 100, min: -100, stdDev: 0, timestamp: startTS, periodMs: periodStringStart, aggKindMoniker: aggregateKindMoniker);
            Assert.AreEqual(6, filterInvocationsCount);
        }