Packages
new_relic_agent
1.31.0
1.40.3
1.40.2
1.40.1
1.40.0
1.39.0
1.38.0
1.37.0
1.36.0
1.35.0
1.34.0
1.33.0
1.32.0
1.31.1
1.31.0
1.30.0
1.29.0
1.28.0
1.27.8
1.27.7
1.27.6
1.27.5
1.27.4
1.27.3
1.27.2
1.27.1
1.27.0
1.26.0
1.25.3
1.25.2
1.25.1
1.25.0
1.24.5
1.24.4
1.24.3
1.24.2
1.24.1
1.24.0
1.24.0-rc.10
1.24.0-rc.9
1.24.0-rc.8
1.24.0-rc.7
1.23.6
1.23.5
1.23.4
1.23.3
1.23.2
1.23.1
1.23.0
1.23.0-rc.6
1.23.0-rc.5
1.23.0-rc.4
1.23.0-rc.3
1.23.0-rc.2
1.23.0-rc.1
1.22.6
1.22.5
1.22.4
1.22.3
1.22.2
1.22.1
1.22.0
1.21.2
1.21.1
1.21.0
1.21.0-rc.3
1.21.0-rc.1
1.20.0
1.20.0-rc.1
1.19.7
1.19.6
1.19.5
1.19.4
1.19.3
1.19.2
1.19.1
1.19.0
1.18.5
1.18.4
1.18.3
1.18.2
1.18.1
1.18.0
1.18.0-rc.5
1.18.0-rc.4
1.18.0-rc.2
1.18.0-rc.1
1.17.1
1.17.0
1.17.0-rc.4
1.17.0-rc.3
1.17.0-rc.2
1.17.0-rc.1
1.17.0-rc.0
1.16.7
1.16.6
1.16.5
1.16.4
1.16.3
1.16.2
1.16.1
1.16.0
1.16.0-rc.2
1.16.0-rc.1
1.16.0-rc.0
1.15.0
1.15.0-rc.3
1.15.0-rc.2
1.15.0-rc.1
1.14.0
1.13.1
1.13.0
1.12.0
1.11.0
1.10.2
1.10.1
1.10.0
1.9.12
1.9.11
1.9.10
1.9.9
1.9.8
1.9.7
1.9.6
1.9.5
1.9.4
1.9.3
1.9.2
1.9.1
1.9.0
1.8.0
1.7.0
1.6.2
1.6.1
1.6.0
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.0
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
New Relic's Open-Source Elixir Agent
Current section
Files
Jump to
Current section
Files
lib/new_relic/distributed_trace/backoff_sampler.ex
defmodule NewRelic.DistributedTrace.BackoffSampler do
use GenServer
alias NewRelic.Harvest.Collector.AgentRun
# This GenServer tracks the sampling rate across sampling periods,
# which is used to determine when to sample a Distributed Trace.
# State is stored in erlang `counters` which are super fast
@moduledoc false
def start_link(_) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
# Counter indexes
@size 5
@cycle_number 1
@sampled_true_count 2
@decided_count 3
@decided_count_last 4
@sampling_target 5
def init(:ok) do
NewRelic.sample_process()
:persistent_term.put({__MODULE__, :counter}, new(@size, []))
put(@sampling_target, AgentRun.lookup(:sampling_target) || 10)
trigger_next_cycle()
{:ok, %{}}
end
def sample? do
calculate(%{
cycle_number: get(@cycle_number),
sampled_true_count: get(@sampled_true_count),
decided_count: get(@decided_count),
decided_count_last: get(@decided_count_last),
sampling_target: get(@sampling_target)
})
end
def handle_info(:cycle, state) do
cycle()
trigger_next_cycle()
{:noreply, state}
end
def reset() do
put(@cycle_number, 0)
put(@decided_count_last, 0)
put(@decided_count, 0)
put(@sampled_true_count, 0)
end
def cycle() do
incr(@cycle_number)
put(@decided_count_last, get(@decided_count))
put(@decided_count, 0)
put(@sampled_true_count, 0)
end
def calculate(state) do
sampled = do_sample?(state)
update_state(sampled)
sampled
end
def do_sample?(%{
cycle_number: 0,
sampled_true_count: sampled_true_count,
sampling_target: sampling_target
}) do
sampled_true_count < sampling_target
end
def do_sample?(%{
sampled_true_count: sampled_true_count,
sampling_target: sampling_target,
decided_count_last: decided_count_last
})
when sampled_true_count < sampling_target do
random(decided_count_last) < sampling_target
end
def do_sample?(%{
sampled_true_count: sampled_true_count,
sampling_target: sampling_target,
decided_count: decided_count
}) do
random(decided_count) <
:math.pow(sampling_target, sampling_target / sampled_true_count) -
:math.pow(sampling_target, 0.5)
end
def trigger_next_cycle() do
cycle_period = AgentRun.lookup(:sampling_target_period) || 60_000
Process.send_after(__MODULE__, :cycle, cycle_period)
end
def update_state(false = _sampled?) do
incr(@decided_count)
end
def update_state(true = _sampled?) do
incr(@decided_count)
incr(@sampled_true_count)
end
def random(0), do: 0
def random(n), do: :rand.uniform(n)
@compile {:inline, new: 2, incr: 1, put: 2, get: 1, pt: 0}
defp new(size, opts), do: :counters.new(size, opts)
defp incr(index), do: :counters.add(pt(), index, 1)
defp put(index, value), do: :counters.put(pt(), index, value)
defp get(index), do: :counters.get(pt(), index)
def pt(), do: :persistent_term.get({__MODULE__, :counter})
end