Skip to content

Commit c50aab8

Browse files
c24tcodeboten
andauthored
Clean up ProbabilitySampler for 64 bit trace IDs (open-telemetry#238)
Co-authored-by: alrex <alrex.boten@gmail.com>
1 parent 19d573a commit c50aab8

File tree

2 files changed

+32
-15
lines changed

2 files changed

+32
-15
lines changed

opentelemetry-api/src/opentelemetry/trace/sampling.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,13 @@ def __init__(self, rate: float):
8282
self._rate = rate
8383
self._bound = self.get_bound_for_rate(self._rate)
8484

85-
# The sampler checks the last 8 bytes of the trace ID to decide whether to
86-
# sample a given trace.
87-
CHECK_BYTES = 0xFFFFFFFFFFFFFFFF
85+
# For compatibility with 64 bit trace IDs, the sampler checks the 64
86+
# low-order bits of the trace ID to decide whether to sample a given trace.
87+
TRACE_ID_LIMIT = (1 << 64) - 1
8888

8989
@classmethod
9090
def get_bound_for_rate(cls, rate: float) -> int:
91-
return round(rate * (cls.CHECK_BYTES + 1))
91+
return round(rate * (cls.TRACE_ID_LIMIT + 1))
9292

9393
@property
9494
def rate(self) -> float:
@@ -115,7 +115,7 @@ def should_sample(
115115
if parent_context is not None:
116116
return Decision(parent_context.trace_options.sampled)
117117

118-
return Decision(trace_id & self.CHECK_BYTES < self.bound)
118+
return Decision(trace_id & self.TRACE_ID_LIMIT < self.bound)
119119

120120

121121
# Samplers that ignore the parent sampling decision and never/always sample.

opentelemetry-api/tests/trace/test_sampling.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import sys
1516
import unittest
1617

1718
from opentelemetry import trace
@@ -137,7 +138,7 @@ def test_probability_sampler(self):
137138
trace.SpanContext(
138139
0xDEADBEF0, 0xDEADBEF1, trace_options=TO_DEFAULT
139140
),
140-
0x8000000000000000,
141+
0x7FFFFFFFFFFFFFFF,
141142
0xDEADBEEF,
142143
"span name",
143144
).sampled
@@ -147,7 +148,7 @@ def test_probability_sampler(self):
147148
trace.SpanContext(
148149
0xDEADBEF0, 0xDEADBEF1, trace_options=TO_SAMPLED
149150
),
150-
0x8000000000000001,
151+
0x8000000000000000,
151152
0xDEADBEEF,
152153
"span name",
153154
).sampled
@@ -189,14 +190,13 @@ def test_probability_sampler_limits(self):
189190
sampling.ProbabilitySampler.get_bound_for_rate(2 ** -64), 0x1
190191
)
191192

192-
# Sample every trace with (last 8 bytes of) trace ID less than
193-
# 0xffffffffffffffff. In principle this is the highest possible
194-
# sampling rate less than 1, but we can't actually express this rate as
195-
# a float!
193+
# Sample every trace with trace ID less than 0xffffffffffffffff. In
194+
# principle this is the highest possible sampling rate less than 1, but
195+
# we can't actually express this rate as a float!
196196
#
197197
# In practice, the highest possible sampling rate is:
198198
#
199-
# round(sys.float_info.epsilon * 2 ** 64)
199+
# 1 - sys.float_info.epsilon
200200

201201
almost_always_on = sampling.ProbabilitySampler(1 - 2 ** -64)
202202
self.assertTrue(
@@ -212,12 +212,29 @@ def test_probability_sampler_limits(self):
212212
# self.assertFalse(
213213
# almost_always_on.should_sample(
214214
# None,
215-
# 0xffffffffffffffff,
216-
# 0xdeadbeef,
215+
# 0xFFFFFFFFFFFFFFFF,
216+
# 0xDEADBEEF,
217217
# "span name",
218218
# ).sampled
219219
# )
220220
# self.assertEqual(
221221
# sampling.ProbabilitySampler.get_bound_for_rate(1 - 2 ** -64)),
222-
# 0xffffffffffffffff,
222+
# 0xFFFFFFFFFFFFFFFF,
223223
# )
224+
225+
# Check that a sampler with the highest effective sampling rate < 1
226+
# refuses to sample traces with trace ID 0xffffffffffffffff.
227+
almost_almost_always_on = sampling.ProbabilitySampler(
228+
1 - sys.float_info.epsilon
229+
)
230+
self.assertFalse(
231+
almost_almost_always_on.should_sample(
232+
None, 0xFFFFFFFFFFFFFFFF, 0xDEADBEEF, "span name"
233+
).sampled
234+
)
235+
# Check that the higest effective sampling rate is actually lower than
236+
# the highest theoretical sampling rate. If this test fails the test
237+
# above is wrong.
238+
self.assertLess(
239+
almost_almost_always_on.bound, 0xFFFFFFFFFFFFFFFF,
240+
)

0 commit comments

Comments
 (0)