// @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); } } }
// @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"); } }
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); }
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}.")); } }
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; }
private static IMetricSeriesAggregator UnwrapAggregator(WeakReference <IMetricSeriesAggregator> aggregatorWeakRef) { if (aggregatorWeakRef != null) { IMetricSeriesAggregator aggregatorHardRef = null; if (aggregatorWeakRef.TryGetTarget(out aggregatorHardRef)) { return(aggregatorHardRef); } } return(null); }
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); }
/// <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}."); } }
private IMetricSeriesAggregator GetNewOrRecycledAggregatorInstance(MetricAggregationCycleKind aggregationCycleKind) { IMetricSeriesAggregator aggregator = this.GetRecycledAggregatorInstance(aggregationCycleKind); return(aggregator ?? this.configuration.CreateNewAggregator(this, aggregationCycleKind)); }
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. } }
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); }