class NewRelic::Agent::AdaptiveSampler

Public Class Methods

new(target_samples = 10, period_duration = 60) click to toggle source
# File lib/new_relic/agent/adaptive_sampler.rb, line 8
def initialize(target_samples = 10, period_duration = 60)
  @target = target_samples
  @seen = 0
  @seen_last = 0
  @sampled_count = 0
  @period_duration = period_duration
  @first_period = true
  @period_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  @lock = Mutex.new
  register_config_callbacks
end

Public Instance Methods

exponential_backoff() click to toggle source
# File lib/new_relic/agent/adaptive_sampler.rb, line 42
def exponential_backoff
  @target**(@target.to_f / @sampled_count) - @target**0.5
end
sampled?() click to toggle source

Called at the beginning of each transaction, increments seen and returns a boolean indicating if we should mark the transaction as sampled. This uses the adaptive sampling algorithm.

# File lib/new_relic/agent/adaptive_sampler.rb, line 23
def sampled?
  @lock.synchronize do
    reset_if_period_expired!
    sampled = if @first_period
      @sampled_count < 10
    elsif @sampled_count < @target
      rand(@seen_last) < @target
    else
      # you've met the target and need to exponentially back off
      rand(@seen) < exponential_backoff
    end

    @sampled_count += 1 if sampled
    @seen += 1

    sampled
  end
end
stats() click to toggle source
# File lib/new_relic/agent/adaptive_sampler.rb, line 46
def stats
  @lock.synchronize do
    {
      target: @target,
      seen: @seen,
      seen_last: @seen_last,
      sampled_count: @sampled_count
    }
  end
end

Private Instance Methods

register_config_callbacks() click to toggle source
# File lib/new_relic/agent/adaptive_sampler.rb, line 72
def register_config_callbacks
  register_sampling_target_callback
  register_sampling_period_callback
end
register_sampling_period_callback() click to toggle source
# File lib/new_relic/agent/adaptive_sampler.rb, line 92
def register_sampling_period_callback
  NewRelic::Agent.config.register_callback(:sampling_target_period_in_seconds) do |period_duration|
    period_changed = false
    @lock.synchronize do
      if @period_duration != period_duration
        @period_duration = period_duration
        period_changed = true
      end
    end
    if period_changed
      NewRelic::Agent.logger.debug("Sampling period set to: #{period_duration}")
    end
  end
end
register_sampling_target_callback() click to toggle source
# File lib/new_relic/agent/adaptive_sampler.rb, line 77
def register_sampling_target_callback
  NewRelic::Agent.config.register_callback(:sampling_target) do |target|
    target_changed = false
    @lock.synchronize do
      if @target != target
        @target = target
        target_changed = true
      end
    end
    if target_changed
      NewRelic::Agent.logger.debug("Sampling target set to: #{target}")
    end
  end
end
reset_if_period_expired!() click to toggle source
# File lib/new_relic/agent/adaptive_sampler.rb, line 59
def reset_if_period_expired!
  now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  return unless @period_start + @period_duration <= now

  elapsed_periods = Integer((now - @period_start) / @period_duration)
  @period_start += elapsed_periods * @period_duration

  @first_period = false
  @seen_last = elapsed_periods > 1 ? 0 : @seen
  @seen = 0
  @sampled_count = 0
end