Skip to content

Commit d960f15

Browse files
authored
[APMAPI-1258] Adjust trace sampling formula (#4616)
1 parent f352621 commit d960f15

File tree

7 files changed

+34
-7
lines changed

7 files changed

+34
-7
lines changed

lib/datadog/tracing/distributed/b3_multi.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def extract(data)
5555
span_id = Helpers.parse_hex_id(fetcher[@span_id_key])
5656

5757
# Return early if this propagation is not valid
58-
return if span_id.nil? || span_id <= 0 || span_id >= Tracing::Utils::EXTERNAL_MAX_ID
58+
return if span_id.nil? || span_id <= 0 || span_id > Tracing::Utils::EXTERNAL_MAX_ID
5959

6060
# We don't need to try and convert sampled since B3 supports 0/1 (AUTO_REJECT/AUTO_KEEP)
6161
sampling_priority = Helpers.parse_decimal_id(fetcher[@sampled_key])

lib/datadog/tracing/distributed/b3_single.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def extract(env)
5454

5555
span_id = Helpers.parse_hex_id(parts[1]) if parts.length > 1
5656
# Return early if this propagation is not valid
57-
return if span_id.nil? || span_id <= 0 || span_id >= Tracing::Utils::EXTERNAL_MAX_ID
57+
return if span_id.nil? || span_id <= 0 || span_id > Tracing::Utils::EXTERNAL_MAX_ID
5858

5959
sampling_priority = Helpers.parse_decimal_id(parts[2]) if parts.length > 2
6060

lib/datadog/tracing/distributed/datadog.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def parse_trace_id(fetcher_object)
8989
trace_id = Helpers.parse_decimal_id(fetcher_object[@trace_id_key])
9090

9191
return unless trace_id
92-
return if trace_id <= 0 || trace_id >= Tracing::Utils::EXTERNAL_MAX_ID
92+
return if trace_id <= 0 || trace_id > Tracing::Utils::EXTERNAL_MAX_ID
9393

9494
trace_id
9595
end
@@ -98,7 +98,7 @@ def parse_parent_id(fetcher_object)
9898
parent_id = Helpers.parse_decimal_id(fetcher_object[@parent_id_key])
9999

100100
return unless parent_id
101-
return if parent_id <= 0 || parent_id >= Tracing::Utils::EXTERNAL_MAX_ID
101+
return if parent_id <= 0 || parent_id > Tracing::Utils::EXTERNAL_MAX_ID
102102

103103
parent_id
104104
end

lib/datadog/tracing/sampling/rate_sampler.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module Sampling
99
# {Datadog::Tracing::Sampling::RateSampler} is based on a sample rate.
1010
class RateSampler < Sampler
1111
KNUTH_FACTOR = 1111111111111111111
12+
UINT64_MODULO = (1 << 64)
1213

1314
# Initialize a {Datadog::Tracing::Sampling::RateSampler}.
1415
# This sampler keeps a random subset of the traces. Its main purpose is to
@@ -39,7 +40,7 @@ def sample_rate=(sample_rate)
3940
end
4041

4142
def sample?(trace)
42-
((trace.id * KNUTH_FACTOR) % Tracing::Utils::EXTERNAL_MAX_ID) <= @sampling_id_threshold
43+
((trace.id * KNUTH_FACTOR) % UINT64_MODULO) <= @sampling_id_threshold
4344
end
4445

4546
def sample!(trace)

lib/datadog/tracing/utils.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module Utils
2424

2525
# While we only generate 63-bit integers due to limitations in other languages, we support
2626
# parsing 64-bit integers for distributed tracing since an upstream system may generate one
27-
EXTERNAL_MAX_ID = 1 << 64
27+
EXTERNAL_MAX_ID = (1 << 64) - 1
2828

2929
# We use a custom random number generator because we want no interference
3030
# with the default one. Using the default prng, we could break code that

sig/datadog/tracing/sampling/rate_sampler.rbs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ module Datadog
22
module Tracing
33
module Sampling
44
class RateSampler < Sampler
5-
KNUTH_FACTOR: 1111111111111111111
5+
KNUTH_FACTOR: Integer
6+
UINT64_MODULO: Integer
67
def initialize: (?::Float sample_rate, ?decision: untyped?) -> void
78

89
def sample_rate: (*untyped _) -> untyped

spec/datadog/tracing/sampling/rate_sampler_spec.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,30 @@
122122
expect(trace.get_tag('_dd.p.dm')).to be_nil
123123
end
124124
end
125+
126+
context 'with specific trace IDs and 0.5 sample rate' do
127+
let(:sample_rate) { 0.5 }
128+
let(:trace_expectations) do
129+
{
130+
12078589664685934330 => false,
131+
13794769880582338323 => true,
132+
1882305164521835798 => true,
133+
5198373796167680436 => false,
134+
6272545487220484606 => true,
135+
8696342848850656916 => true,
136+
18444899399302180860 => false,
137+
18444899399302180862 => true,
138+
9223372036854775808 => true, # Lands exactly on the sampling threshold 0.5 * MAX_UINT64
139+
}
140+
end
141+
142+
it 'produces deterministic sampling results' do
143+
trace_expectations.each do |id, expected|
144+
trace = Datadog::Tracing::TraceOperation.new(id: id)
145+
expect(sampler.sample!(trace)).to eq(expected),
146+
"Expected trace ID #{id} to be #{expected ? 'sampled' : 'not sampled'}"
147+
end
148+
end
149+
end
125150
end
126151
end

0 commit comments

Comments
 (0)