Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions sentry_sdk/tracing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1577,23 +1577,39 @@ def _make_sampling_decision(
return False, 0.0, None, "sample_rate"

# Adjust sample rate if we're under backpressure
sample_rate_before_backpressure = sample_rate
if client.monitor:
sample_rate /= 2**client.monitor.downsample_factor

if not sample_rate:
logger.debug(f"[Tracing] Discarding {name} because backpressure")
return False, 0.0, None, "backpressure"

# Make the actual decision
sampled = sample_rand < sample_rate

if sampled:
logger.debug(f"[Tracing] Starting {name}")
outcome = None

else:
logger.debug(
f"[Tracing] Discarding {name} because it's not included in the random sample (sampling rate = {sample_rate})"
)
outcome = "sample_rate"
# Determine why exactly the span will not be sampled. If we've lowered
# the effective sample_rate because of backpressure, check whether the
# span would've been sampled if backpressure wasn't active. If that's the
# case, backpressure is the actual reason, otherwise just pure sampling
# rate.
if (
sample_rate_before_backpressure != sample_rate
and sample_rand < sample_rate_before_backpressure
):
logger.debug(f"[Tracing] Discarding {name} because backpressure")
outcome = "backpressure"

else:
logger.debug(
f"[Tracing] Discarding {name} because it's not included in the random sample (sampling rate = {sample_rate})"
)
outcome = "sample_rate"

return sampled, sample_rate, sample_rand, outcome

Expand Down
63 changes: 63 additions & 0 deletions tests/tracing/test_span_streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,69 @@ def test_continue_trace_unsampled(sentry_init, capture_items):
assert len(spans) == 0


@pytest.mark.parametrize(
("sample_rand", "expected_sampled", "expected_outcome"),
[
("0.100000", True, None),
("0.250000", False, "backpressure"),
("0.300000", False, "backpressure"),
("0.500000", False, "sample_rate"),
("0.700000", False, "sample_rate"),
],
)
def test_backpressure_outcome(
sentry_init,
capture_items,
capture_record_lost_event_calls,
sample_rand,
expected_sampled,
expected_outcome,
):
sentry_init(
traces_sample_rate=0.5,
enable_backpressure_handling=True,
_experiments={"trace_lifecycle": "stream"},
)

items = capture_items("span")
record_lost_event_calls = capture_record_lost_event_calls()

client = sentry_sdk.get_client()
client.monitor._downsample_factor = 1

trace_id = "0af7651916cd43dd8448eb211c80319c"
parent_span_id = "b7ad6b7169203331"

sentry_sdk.traces.continue_trace(
{
"sentry-trace": f"{trace_id}-{parent_span_id}",
"baggage": f"sentry-trace_id={trace_id},sentry-sample_rand={sample_rand}",
}
)

with sentry_sdk.traces.start_span(name="span") as span:
pass

sentry_sdk.get_client().flush()
spans = [item.payload for item in items]

# Original traces_sample_rate is 0.5, downsampled sample rate is 0.25, so:
# - sample_rand < 0.25 -> sampled
# - 0.25 < sample_rand < 0.5 -> unsampled because of backpressure (would've been sampled if no backpressure)
# - 0.5 < sample_rand -> unsampled because of sampling rate

assert span.sampled is expected_sampled

if expected_sampled:
assert len(spans) == 1
assert not record_lost_event_calls
else:
assert len(spans) == 0
assert record_lost_event_calls == [
(expected_outcome, "span", None, 1),
]


def test_unsampled_spans_produce_client_report(
sentry_init, capture_items, capture_record_lost_event_calls
):
Expand Down
Loading