Skip to content

Commit c789dcb

Browse files
authored
internal: refactor DatadogSampler, subclass from RateByServiceSampler
There are implementation details of these two samplers which need to be shared having DatadogSampler subclass from RateByServiceSampler simplifies coordination
1 parent 425964a commit c789dcb

File tree

2 files changed

+23
-34
lines changed

2 files changed

+23
-34
lines changed

ddtrace/sampler.py

+20-25
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,24 @@ def set_sample_rate(
138138
# type: (...) -> None
139139
self._by_service_samplers[self._key(service, env)] = RateSampler(sample_rate)
140140

141+
def _set_priority(self, span, priority):
142+
# type: (Span, int) -> None
143+
span.context.sampling_priority = priority
144+
span.sampled = priority > 0 # Positive priorities mean it was kept
145+
141146
def sample(self, span):
142147
# type: (Span) -> bool
143148
tags = span.tracer.tags if span.tracer else {}
144149
env = tags[ENV_KEY] if ENV_KEY in tags else None
145150
key = self._key(span.service, env)
146151

147152
sampler = self._by_service_samplers.get(key, self._by_service_samplers[self._default_key])
153+
sampled = sampler.sample(span)
154+
priority = AUTO_KEEP if sampled else AUTO_REJECT
155+
148156
span.set_metric(SAMPLING_AGENT_DECISION, sampler.sample_rate)
149-
return sampler.sample(span)
157+
self._set_priority(span, priority)
158+
return sampled
150159

151160
def update_rate_by_service_sample_rates(self, rate_by_service):
152161
# type: (Dict[str, float]) -> None
@@ -157,7 +166,7 @@ def update_rate_by_service_sample_rates(self, rate_by_service):
157166
self._by_service_samplers = new_by_service_samplers
158167

159168

160-
class DatadogSampler(BasePrioritySampler):
169+
class DatadogSampler(RateByServiceSampler):
161170
"""
162171
Default sampler used by Tracer for determining if a trace should be kept or dropped.
163172
@@ -188,7 +197,7 @@ class DatadogSampler(BasePrioritySampler):
188197
provided. It is not used when the agent supplied sample rates are used.
189198
"""
190199

191-
__slots__ = ("_agent_sampler", "limiter", "rules")
200+
__slots__ = ("limiter", "rules")
192201

193202
NO_RATE_LIMIT = -1
194203
DEFAULT_RATE_LIMIT = 100
@@ -212,6 +221,9 @@ def __init__(
212221
applied to them, (default: ``100``)
213222
:type rate_limit: :obj:`int`
214223
"""
224+
# Use default sample rate of 1.0
225+
super(DatadogSampler, self).__init__()
226+
215227
if default_sample_rate is None:
216228
sample_rate = get_env("trace", "sample_rate")
217229
if sample_rate is not None:
@@ -238,9 +250,6 @@ def __init__(
238250
if default_sample_rate is not None:
239251
self.rules.append(SamplingRule(sample_rate=default_sample_rate))
240252

241-
# If no rules are configured (or match), fallback to agent based sampling
242-
self._agent_sampler = RateByServiceSampler()
243-
244253
# Configure rate limiter
245254
self.limiter = RateLimiter(rate_limit)
246255

@@ -250,11 +259,12 @@ def __init__(
250259
def default_sampler(self):
251260
if self.rules:
252261
return self.rules[-1]
253-
return self._agent_sampler
262+
return self
254263

255264
def __str__(self):
256-
return "{}(agent_sampler={!r}, limiter={!r}, rules={!r})".format(
257-
self.__class__.__name__, self._agent_sampler, self.limiter, self.rules
265+
rates = {key: sampler.sample_rate for key, sampler in self._by_service_samplers.items()}
266+
return "{}(agent_rates={!r}, limiter={!r}, rules={!r})".format(
267+
self.__class__.__name__, rates, self.limiter, self.rules
258268
)
259269

260270
__repr__ = __str__
@@ -280,16 +290,6 @@ def _parse_rules_from_env_variable(self, rules):
280290
sampling_rules.append(sampling_rule)
281291
return sampling_rules
282292

283-
def update_rate_by_service_sample_rates(self, sample_rates):
284-
# type: (Dict[str, float]) -> None
285-
# Pass through the call to our RateByServiceSampler
286-
self._agent_sampler.update_rate_by_service_sample_rates(sample_rates)
287-
288-
def _set_priority(self, span, priority):
289-
# type: (Span, int) -> None
290-
span.context.sampling_priority = priority
291-
span.sampled = priority > 0 # Positive priorities mean it was kept
292-
293293
def sample(self, span):
294294
# type: (Span) -> bool
295295
"""
@@ -313,12 +313,7 @@ def sample(self, span):
313313
break
314314
else:
315315
# No rules matches so use agent based sampling
316-
if self._agent_sampler.sample(span):
317-
self._set_priority(span, AUTO_KEEP)
318-
return True
319-
else:
320-
self._set_priority(span, AUTO_REJECT)
321-
return False
316+
return super(DatadogSampler, self).sample(span)
322317

323318
# DEV: This should never happen, but since the type is Optional we have to check
324319
if not matching_rule:

tests/tracer/test_sampler.py

+3-9
Original file line numberDiff line numberDiff line change
@@ -622,39 +622,33 @@ def test_datadog_sampler_init():
622622
assert sampler.rules == []
623623
assert isinstance(sampler.limiter, RateLimiter)
624624
assert sampler.limiter.rate_limit == DatadogSampler.DEFAULT_RATE_LIMIT
625-
assert isinstance(sampler._agent_sampler, RateByServiceSampler)
626625

627626
# With rules
628627
rule = SamplingRule(sample_rate=1)
629628
sampler = DatadogSampler(rules=[rule])
630629
assert sampler.rules == [rule]
631630
assert sampler.limiter.rate_limit == DatadogSampler.DEFAULT_RATE_LIMIT
632-
assert isinstance(sampler._agent_sampler, RateByServiceSampler)
633631

634632
# With rate limit
635633
sampler = DatadogSampler(rate_limit=10)
636634
assert sampler.limiter.rate_limit == 10
637-
assert isinstance(sampler._agent_sampler, RateByServiceSampler)
638635

639636
# With default_sample_rate
640637
sampler = DatadogSampler(default_sample_rate=0.5)
641638
assert sampler.limiter.rate_limit == DatadogSampler.DEFAULT_RATE_LIMIT
642639
assert sampler.rules == [SamplingRule(sample_rate=0.5)]
643-
assert isinstance(sampler._agent_sampler, RateByServiceSampler)
644640

645641
# From env variables
646642
with override_env(dict(DD_TRACE_SAMPLE_RATE="0.5", DD_TRACE_RATE_LIMIT="10")):
647643
sampler = DatadogSampler()
648644
assert sampler.limiter.rate_limit == 10
649645
assert sampler.rules == [SamplingRule(sample_rate=0.5)]
650-
assert isinstance(sampler._agent_sampler, RateByServiceSampler)
651646

652647
# DD_TRACE_SAMPLE_RATE=0
653648
with override_env(dict(DD_TRACE_SAMPLE_RATE="0")):
654649
sampler = DatadogSampler()
655650
assert sampler.limiter.rate_limit == DatadogSampler.DEFAULT_RATE_LIMIT
656651
assert sampler.rules == [SamplingRule(sample_rate=0)]
657-
assert isinstance(sampler._agent_sampler, RateByServiceSampler)
658652

659653
# Invalid env vars
660654
with override_env(dict(DD_TRACE_SAMPLE_RATE="asdf")):
@@ -683,7 +677,7 @@ def test_datadog_sampler_init():
683677
assert sampler.rules == [rule_1, rule_2, rule_3, SamplingRule(sample_rate=0.75)]
684678

685679

686-
@mock.patch("ddtrace.sampler.RateByServiceSampler.sample")
680+
@mock.patch("ddtrace.sampler.RateSampler.sample")
687681
def test_datadog_sampler_sample_no_rules(mock_sample, dummy_tracer):
688682
sampler = DatadogSampler()
689683
dummy_tracer.configure(sampler=sampler)
@@ -939,7 +933,7 @@ def test_datadog_sampler_update_rate_by_service_sample_rates(dummy_tracer):
939933
for case in cases:
940934
sampler.update_rate_by_service_sample_rates(case)
941935
rates = {}
942-
for k, v in iteritems(sampler._agent_sampler._by_service_samplers):
936+
for k, v in iteritems(sampler._by_service_samplers):
943937
rates[k] = v.sample_rate
944938
assert case == rates, "%s != %s" % (case, rates)
945939

@@ -949,6 +943,6 @@ def test_datadog_sampler_update_rate_by_service_sample_rates(dummy_tracer):
949943
for case in cases:
950944
sampler.update_rate_by_service_sample_rates(case)
951945
rates = {}
952-
for k, v in iteritems(sampler._agent_sampler._by_service_samplers):
946+
for k, v in iteritems(sampler._by_service_samplers):
953947
rates[k] = v.sample_rate
954948
assert case == rates, "%s != %s" % (case, rates)

0 commit comments

Comments
 (0)