Skip to content

Commit 46cf5ce

Browse files
ref: Flush trace bucket when segment span finishes
1 parent 8669777 commit 46cf5ce

3 files changed

Lines changed: 34 additions & 17 deletions

File tree

sentry_sdk/_span_batcher.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def _flush_loop(self) -> None:
100100
self._flush()
101101
self._last_full_flush = time.monotonic()
102102

103-
def add(self, span: "SpanJSON") -> None:
103+
def add(self, span: "SpanJSON", flush_trace_bucket: "bool" = False) -> None:
104104
# Bail out if the current thread is already executing batcher code.
105105
# This prevents deadlocks when code running inside the batcher (e.g.
106106
# _add_to_envelope during flush, or _flush_event.wait/set) triggers
@@ -129,7 +129,8 @@ def add(self, span: "SpanJSON") -> None:
129129
self._running_size[span["trace_id"]] += self._estimate_size(span)
130130

131131
if (
132-
size + 1 >= self.MAX_BEFORE_FLUSH
132+
flush_trace_bucket
133+
or size + 1 >= self.MAX_BEFORE_FLUSH
133134
or self._running_size[span["trace_id"]]
134135
>= self.MAX_BYTES_BEFORE_FLUSH
135136
):

sentry_sdk/client.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,12 @@ def _capture_telemetry(
12701270
if serialized is None:
12711271
return
12721272

1273+
if ty == "log":
1274+
self.log_batcher.add(serialized) # type: ignore
1275+
1276+
elif ty == "metric":
1277+
self.metrics_batcher.add(serialized) # type: ignore
1278+
12731279
elif ty == "span" and isinstance(telemetry, StreamedSpan):
12741280
# Reset the span to its original value before we attempted
12751281
# to call the `before_send_span` callback
@@ -1292,21 +1298,12 @@ def _capture_telemetry(
12921298

12931299
serialized = telemetry._to_json()
12941300

1295-
batcher = None
1296-
if ty == "log":
1297-
batcher = self.log_batcher
1298-
1299-
elif ty == "metric":
1300-
batcher = self.metrics_batcher
1301-
1302-
elif ty == "span":
1303-
# We need a reference to the segment span in the batcher to populate
1304-
# the dynamic sampling context (DSC)
1305-
serialized["_segment_span"] = telemetry._segment # type: ignore
1306-
batcher = self.span_batcher
1307-
1308-
if batcher is not None:
1309-
batcher.add(serialized) # type: ignore
1301+
# We need a reference to the segment span in the batcher to populate
1302+
# the dynamic sampling context (DSC)
1303+
serialized["_segment_span"] = telemetry._segment # type: ignore
1304+
self.span_batcher.add(
1305+
serialized, flush_trace_bucket=telemetry._is_segment()
1306+
) # type: ignore
13101307

13111308
def _capture_log(self, log: "Optional[Log]", scope: "Scope") -> None:
13121309
self._capture_telemetry(log, "log", scope)

tests/tracing/test_span_batcher.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,25 @@ def test_transport_format(sentry_init, capture_envelopes):
432432
assert value["type"] in ("string", "boolean", "integer", "double", "array")
433433

434434

435+
def test_trace_bucket_flushes_when_segment_ends(
436+
sentry_init, capture_items, monkeypatch
437+
):
438+
"""All currently completed spans in a trace are flushed when the segment is finished."""
439+
monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000)
440+
441+
sentry_init(traces_sample_rate=1.0, trace_lifecycle="stream")
442+
items = capture_items("span")
443+
444+
with sentry_sdk.traces.start_span(name="segment span"):
445+
with sentry_sdk.traces.start_span(name="child"):
446+
pass
447+
448+
time.sleep(0.1)
449+
450+
assert len(items) == 3
451+
assert items[0].payload["name"] == "span"
452+
453+
435454
@pytest.mark.skipif(
436455
sys.platform == "win32"
437456
or not hasattr(os, "fork")

0 commit comments

Comments
 (0)