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