/// <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; } } 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); } }
/// <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 AdaptiveSampler(SamplingPercentageEstimatorSettings settings) { if (settings == null) { throw new ArgumentNullException("settings"); } this.settings = settings; 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); }