/// <summary> /// Initializes a new instance of the <see cref="SamplingPercentageEstimatorTelemetryProcessor"/> class. /// <param name="settings">Dynamic sampling estimator settings.</param> /// <param name="callback">Callback to invoke every time sampling percentage is evaluated.</param> /// <param name="next">Next TelemetryProcessor in call chain.</param> /// </summary> public SamplingPercentageEstimatorTelemetryProcessor( SamplingPercentageEstimatorSettings settings, AdaptiveSamplingPercentageEvaluatedCallback callback, ITelemetryProcessor next) { if (settings == null) { throw new ArgumentNullException("settings"); } if (next == null) { throw new ArgumentNullException("next"); } this.evaluationCallback = callback; this.settings = settings; this.next = next; this.currenSamplingRate = settings.EffectiveInitialSamplingRate; this.itemCount = new ExponentialMovingAverageCounter(settings.EffectiveMovingAverageRatio); this.samplingPercentageLastChangeDateTime = DateTimeOffset.UtcNow; // set evaluation interval to default value if it is negative or zero this.evaluationInterval = this.settings.EffectiveEvaluationInterval; // set up timer to run math to estimate sampling percentage this.evaluationTimer = new Timer( this.EstimateSamplingPercentage, null, this.evaluationInterval, this.evaluationInterval); }
public void AverageValueIsFirstIntervalValuePriorToClosingOfFirstInterval() { var counter = new ExponentialMovingAverageCounter(.1); const int IncrementCount = 3; for (int i = 0; i < IncrementCount; i++) { counter.Increment(); } Assert.AreEqual(IncrementCount, counter.Average); }
public void AverageValueIsFirstIntervalValuePriorToClosingOfFirstInterval() { var counter = new ExponentialMovingAverageCounter(.1); const int IncrementCount = 3; for (int i = 0; i < IncrementCount; i++) { counter.Increment(); } Assert.Equal(IncrementCount, counter.Average); }
public void AverageValueIsMovingAverageAfterClosingOfAtLeastTwoIntervals() { var counter = new ExponentialMovingAverageCounter(.1); const int Increment1Count = 3; const int Increment2Count = 5; for (int i = 0; i < Increment1Count; i++) { counter.Increment(); } counter.StartNewInterval(); for (int i = 0; i < Increment2Count; i++) { counter.Increment(); } counter.StartNewInterval(); Assert.AreEqual(Increment1Count * .9 + Increment2Count * .1, counter.Average, 10); }
public void AverageValueIsMovingAverageAfterClosingOfAtLeastTwoIntervals() { var counter = new ExponentialMovingAverageCounter(.1); const int Increment1Count = 3; const int Increment2Count = 5; for (int i = 0; i < Increment1Count; i++) { counter.Increment(); } counter.StartNewInterval(); for (int i = 0; i < Increment2Count; i++) { counter.Increment(); } counter.StartNewInterval(); Xunit.Assert.Equal(Increment1Count * .9 + Increment2Count * .1, counter.Average, 10); }
public void AverageValueIsZeroPriorToStart() { var counter = new ExponentialMovingAverageCounter(.1); Assert.AreEqual(0, counter.Average); }
/// <summary> /// Callback for sampling percentage evaluation timer. /// </summary> /// <param name="state">Timer state.</param> private void EstimateSamplingPercentage(object state) { // get observed after-sampling eps double observedEps = this.itemCount.StartNewInterval() / this.evaluationInterval.TotalSeconds; // we see events post sampling, so get pre-sampling eps double beforeSamplingEps = observedEps * this.currenSamplingRate; // calculate suggested sampling rate int suggestedSamplingRate = (int)Math.Ceiling(beforeSamplingEps / this.settings.EffectiveMaxTelemetryItemsPerSecond); // adjust suggested rate so that it fits between min and max configured if (suggestedSamplingRate > this.settings.EffectiveMaxSamplingRate) { suggestedSamplingRate = this.settings.EffectiveMaxSamplingRate; } if (suggestedSamplingRate < this.settings.EffectiveMinSamplingRate) { suggestedSamplingRate = this.settings.EffectiveMinSamplingRate; } // see if evaluation interval was changed and apply change if (this.evaluationInterval != this.settings.EffectiveEvaluationInterval) { this.evaluationInterval = this.settings.EffectiveEvaluationInterval; this.evaluationTimer.Change(this.evaluationInterval, this.evaluationInterval); } // check to see if sampling rate needs changes bool samplingPercentageChangeNeeded = suggestedSamplingRate != this.currenSamplingRate; if (samplingPercentageChangeNeeded) { // check to see if enough time passed since last sampling % change if ((DateTimeOffset.UtcNow - this.samplingPercentageLastChangeDateTime) < (suggestedSamplingRate > this.currenSamplingRate ? this.settings.EffectiveSamplingPercentageDecreaseTimeout : this.settings.EffectiveSamplingPercentageIncreaseTimeout)) { samplingPercentageChangeNeeded = false; } } // call evaluation callback if provided if (this.evaluationCallback != null) { // we do not want to crash timer thread knocking out the process // in case customer-provided callback failed try { this.evaluationCallback( observedEps, 100.0 / this.currenSamplingRate, 100.0 / suggestedSamplingRate, samplingPercentageChangeNeeded, this.settings); } catch (Exception exp) { TelemetryChannelEventSource.Log.SamplingCallbackError(exp.ToString()); } } if (samplingPercentageChangeNeeded) { // apply sampling percentage change this.samplingPercentageLastChangeDateTime = DateTimeOffset.UtcNow; this.currenSamplingRate = suggestedSamplingRate; } if (samplingPercentageChangeNeeded || MovingAverageCoefficientChanged(this.itemCount.Coefficient, this.settings.EffectiveMovingAverageRatio)) { // since we're observing event count post sampling and we're about // to change sampling rate or change coefficient, reset counter this.itemCount = new ExponentialMovingAverageCounter(this.settings.EffectiveMovingAverageRatio); } }
public void AverageValueIsZeroPriorToStart() { var counter = new ExponentialMovingAverageCounter(.1); Assert.Equal(0, counter.Average); }
/// <summary> /// Initializes a new instance of the <see cref="SamplingPercentageEstimatorTelemetryProcessor"/> class. /// <param name="settings">Dynamic sampling estimator settings.</param> /// <param name="callback">Callback to invoke every time sampling percentage is evaluated.</param> /// <param name="next">Next TelemetryProcessor in call chain.</param> /// </summary> public SamplingPercentageEstimatorTelemetryProcessor( SamplingPercentageEstimatorSettings settings, AdaptiveSamplingPercentageEvaluatedCallback callback, ITelemetryProcessor next) { if (settings == null) { throw new ArgumentNullException("settings"); } if (next == null) { throw new ArgumentNullException("next"); } this.evaluationCallback = callback; this.settings = settings; this.next = next; this.currenSamplingRate = settings.EffectiveInitialSamplingRate; this.itemCount = new ExponentialMovingAverageCounter(settings.EffectiveMovingAverageRatio); this.samplingPercentageLastChangeDateTime = DateTimeOffset.UtcNow; // set evaluation interval to default value if it is negative or zero this.evaluationInterval = this.settings.EffectiveEvaluationInterval; // set up timer to run math to estimate sampling percentage this.evaluationTimer = new Timer( this.EstimateSamplingPercentage, null, this.evaluationInterval, this.evaluationInterval); }
/// <summary> /// Callback for sampling percentage evaluation timer. /// </summary> /// <param name="state">Timer state.</param> private void EstimateSamplingPercentage(object state) { // get observed after-sampling eps double observedEps = this.itemCount.StartNewInterval() / this.evaluationInterval.TotalSeconds; // we see events post sampling, so get pre-sampling eps double beforeSamplingEps = observedEps * this.currenSamplingRate; // caclulate suggested sampling rate int suggestedSamplingRate = (int)Math.Ceiling(beforeSamplingEps / this.settings.EffectiveMaxTelemetryItemsPerSecond); // adjust suggested rate so that it fits between min and max configured if (suggestedSamplingRate > this.settings.EffectiveMaxSamplingRate) { suggestedSamplingRate = this.settings.EffectiveMaxSamplingRate; } if (suggestedSamplingRate < this.settings.EffectiveMinSamplingRate) { suggestedSamplingRate = this.settings.EffectiveMinSamplingRate; } // see if evaluation interval was changed and apply change if (this.evaluationInterval != this.settings.EffectiveEvaluationInterval) { this.evaluationInterval = this.settings.EffectiveEvaluationInterval; this.evaluationTimer.Change(this.evaluationInterval, this.evaluationInterval); } // check to see if sampling rate needs changes bool samplingPercentageChangeNeeded = suggestedSamplingRate != this.currenSamplingRate; if (samplingPercentageChangeNeeded) { // check to see if enough time passed since last sampling % change if ((DateTimeOffset.UtcNow - this.samplingPercentageLastChangeDateTime) < (suggestedSamplingRate > this.currenSamplingRate ? this.settings.EffectiveSamplingPercentageDecreaseTimeout : this.settings.EffectiveSamplingPercentageIncreaseTimeout)) { samplingPercentageChangeNeeded = false; } } // call evaluation callback if provided if (this.evaluationCallback != null) { // we do not want to crash timer thread knocking out the process // in case customer-provided callback failed try { this.evaluationCallback( observedEps, 100.0 / this.currenSamplingRate, 100.0 / suggestedSamplingRate, samplingPercentageChangeNeeded, this.settings); } catch (Exception exp) { TelemetryChannelEventSource.Log.SamplingCallbackError(exp.ToString()); } } if (samplingPercentageChangeNeeded) { // apply sampling perfcentage change this.samplingPercentageLastChangeDateTime = DateTimeOffset.UtcNow; this.currenSamplingRate = suggestedSamplingRate; } if (samplingPercentageChangeNeeded || MovingAverageCoefficientChanged(this.itemCount.Coefficient, this.settings.EffectiveMovingAverageRatio)) { // since we're observing event count post sampling and we're about // to change sampling rate or change coefficient, reset counter this.itemCount = new ExponentialMovingAverageCounter(this.settings.EffectiveMovingAverageRatio); } }