From b429f5f23be4cb8d1122f893229d08cb1a789e8e Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 08:37:32 +0200 Subject: [PATCH 01/11] feat(batcher): Add hard span limit --- sentry_sdk/_span_batcher.py | 14 ++++++++- tests/tracing/test_span_batcher.py | 46 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/_span_batcher.py b/sentry_sdk/_span_batcher.py index 79285c3386..a7c8778f83 100644 --- a/sentry_sdk/_span_batcher.py +++ b/sentry_sdk/_span_batcher.py @@ -25,7 +25,10 @@ class SpanBatcher(Batcher["SpanJSON"]): # The max limits are all per trace (per bucket). MAX_ENVELOPE_SIZE = 1000 # spans MAX_BEFORE_FLUSH = 1000 + MAX_BEFORE_DROP = 2000 + GLOBAL_MAX_BEFORE_DROP = 10_000 + MAX_BYTES_BEFORE_FLUSH = 5 * 1024 * 1024 # 5 MB FLUSH_WAIT_TIME = 5.0 @@ -44,6 +47,8 @@ def __init__( # envelope. # trace_id -> span buffer self._span_buffer: dict[str, list["SpanJSON"]] = defaultdict(list) + self._span_number: int = 0 + self._running_size: dict[str, int] = defaultdict(lambda: 0) self._capture_func = capture_func self._record_lost_func = record_lost_func @@ -71,6 +76,8 @@ def _reset_in_child() -> None: def _reset_thread_state(self) -> None: self._span_buffer = defaultdict(list) + self._span_number = 0 + self._running_size = defaultdict(lambda: 0) self._running = True @@ -117,7 +124,10 @@ def add(self, span: "SpanJSON") -> None: with self._lock: size = len(self._span_buffer[span["trace_id"]]) - if size >= self.MAX_BEFORE_DROP: + if ( + size >= self.MAX_BEFORE_DROP + or self._span_number >= self.GLOBAL_MAX_BEFORE_DROP + ): self._record_lost_func( reason="queue_overflow", data_category="span", @@ -126,6 +136,8 @@ def add(self, span: "SpanJSON") -> None: return None self._span_buffer[span["trace_id"]].append(span) + self._span_number += 1 + self._running_size[span["trace_id"]] += self._estimate_size(span) if ( diff --git a/tests/tracing/test_span_batcher.py b/tests/tracing/test_span_batcher.py index c21d6cdea4..c112b965be 100644 --- a/tests/tracing/test_span_batcher.py +++ b/tests/tracing/test_span_batcher.py @@ -171,6 +171,47 @@ def test_drop_isolated_per_bucket( assert record_lost_event_calls.count(("queue_overflow", "span", None, 1)) == 1 +def test_drop_after_global_max_reached( + sentry_init, capture_envelopes, capture_record_lost_event_calls, monkeypatch +): + """New spans are dropped if the buffer reaches GLOBAL_MAX_BEFORE_DROP spans.""" + monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BEFORE_DROP", 2) + # set the time-based flush limit to something huge so that we're not flushing + # prematurely + monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000) + + sentry_init( + traces_sample_rate=1.0, + trace_lifecycle="stream", + ) + + envelopes = capture_envelopes() + record_lost_event_calls = capture_record_lost_event_calls() + + with sentry_sdk.traces.start_span(name="span 1"): + pass + with sentry_sdk.traces.start_span(name="span 2"): + pass + with sentry_sdk.traces.start_span(name="span 3"): + pass + + sentry_sdk.traces.new_trace() + with sentry_sdk.traces.start_span(name="span 4"): + pass + + sentry_sdk.flush() + + assert len(envelopes) == 1 + + print("items are", envelopes[0].items[0].payload.json["items"]) + + assert len(envelopes[0].items[0].payload.json["items"]) == 2 + assert envelopes[0].items[0].payload.json["items"][0]["name"] == "span 1" + assert envelopes[0].items[0].payload.json["items"][1]["name"] == "span 2" + + assert record_lost_event_calls.count(("queue_overflow", "span", None, 1)) == 2 + + def test_length_based_flushing(sentry_init, capture_items, monkeypatch): """A flush event is triggered when a bucket contains MAX_BEFORE_FLUSH spans.""" monkeypatch.setattr(SpanBatcher, "MAX_BEFORE_FLUSH", 1) @@ -460,6 +501,8 @@ def test_span_batcher_lock_reset_in_child_after_fork(sentry_init): original_lock.acquire() batcher._span_buffer["test-trace-id"].append(object()) + batcher._span_number = 1 + batcher._running_size["test-trace-id"] = 42 batcher._active.flag = True batcher._flush_event.set() @@ -472,6 +515,8 @@ def test_span_batcher_lock_reset_in_child_after_fork(sentry_init): flusher_reset = batcher._flusher is None and batcher._flusher_pid is None span_buffer_reset = len(batcher._span_buffer) == 0 + span_number_reset = batcher._span_number == 0 + running_size_reset = len(batcher._running_size) == 0 active_reset = not getattr(batcher._active, "flag", False) @@ -484,6 +529,7 @@ def test_span_batcher_lock_reset_in_child_after_fork(sentry_init): and unheld and flusher_reset and span_buffer_reset + and span_number_reset and running_size_reset and active_reset and event_reset From b30af99a7f2e4f8b1f49700a2085f89c1fed13d4 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 08:39:05 +0200 Subject: [PATCH 02/11] decrement span number --- sentry_sdk/_span_batcher.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sentry_sdk/_span_batcher.py b/sentry_sdk/_span_batcher.py index a7c8778f83..c4bacd8c47 100644 --- a/sentry_sdk/_span_batcher.py +++ b/sentry_sdk/_span_batcher.py @@ -240,6 +240,8 @@ def _flush(self, only_pending: bool = False) -> None: envelopes.append(envelope) del self._span_buffer[bucket_id] + self._span_number -= 1 + del self._running_size[bucket_id] for envelope in envelopes: From bde3c70ac23357fe613866480cf3886fab379e0b Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 08:50:57 +0200 Subject: [PATCH 03/11] add another test --- sentry_sdk/_span_batcher.py | 2 +- tests/tracing/test_span_batcher.py | 45 ++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/_span_batcher.py b/sentry_sdk/_span_batcher.py index c4bacd8c47..5f2e8576b9 100644 --- a/sentry_sdk/_span_batcher.py +++ b/sentry_sdk/_span_batcher.py @@ -239,8 +239,8 @@ def _flush(self, only_pending: bool = False) -> None: envelopes.append(envelope) + self._span_number -= len(self._span_buffer[bucket_id]) del self._span_buffer[bucket_id] - self._span_number -= 1 del self._running_size[bucket_id] diff --git a/tests/tracing/test_span_batcher.py b/tests/tracing/test_span_batcher.py index c112b965be..507fef184b 100644 --- a/tests/tracing/test_span_batcher.py +++ b/tests/tracing/test_span_batcher.py @@ -203,8 +203,6 @@ def test_drop_after_global_max_reached( assert len(envelopes) == 1 - print("items are", envelopes[0].items[0].payload.json["items"]) - assert len(envelopes[0].items[0].payload.json["items"]) == 2 assert envelopes[0].items[0].payload.json["items"][0]["name"] == "span 1" assert envelopes[0].items[0].payload.json["items"][1]["name"] == "span 2" @@ -212,6 +210,49 @@ def test_drop_after_global_max_reached( assert record_lost_event_calls.count(("queue_overflow", "span", None, 1)) == 2 +def test_capture_after_flush_with_global_limit( + sentry_init, capture_envelopes, monkeypatch +): + """New spans are captured again after a flush reduces the span number below the global limit.""" + monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BEFORE_DROP", 2) + # set the time-based flush limit to something huge so that we're not flushing + # prematurely + monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000) + + sentry_init( + traces_sample_rate=1.0, + trace_lifecycle="stream", + ) + + envelopes = capture_envelopes() + + with sentry_sdk.traces.start_span(name="span 1"): + pass + with sentry_sdk.traces.start_span(name="span 2"): + pass + + sentry_sdk.traces.new_trace() + with sentry_sdk.traces.start_span(name="span 3"): + pass + + sentry_sdk.flush() + + # The span is captured even though a span was dropped in the same trace. + with sentry_sdk.traces.start_span(name="span 4"): + pass + + sentry_sdk.flush() + + assert len(envelopes) == 2 + + assert len(envelopes[0].items[0].payload.json["items"]) == 2 + assert envelopes[0].items[0].payload.json["items"][0]["name"] == "span 1" + assert envelopes[0].items[0].payload.json["items"][1]["name"] == "span 2" + + assert len(envelopes[1].items[0].payload.json["items"]) == 1 + assert envelopes[1].items[0].payload.json["items"][0]["name"] == "span 4" + + def test_length_based_flushing(sentry_init, capture_items, monkeypatch): """A flush event is triggered when a bucket contains MAX_BEFORE_FLUSH spans.""" monkeypatch.setattr(SpanBatcher, "MAX_BEFORE_FLUSH", 1) From 684d52455250a4a177cba3984e09ef66dbe1eb53 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 09:01:28 +0200 Subject: [PATCH 04/11] feat(batcher): Add global flush trigger based on summed size estimates --- sentry_sdk/_span_batcher.py | 16 ++++-- tests/tracing/test_span_batcher.py | 79 ++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/_span_batcher.py b/sentry_sdk/_span_batcher.py index 5f2e8576b9..ee2cfe8816 100644 --- a/sentry_sdk/_span_batcher.py +++ b/sentry_sdk/_span_batcher.py @@ -30,6 +30,7 @@ class SpanBatcher(Batcher["SpanJSON"]): GLOBAL_MAX_BEFORE_DROP = 10_000 MAX_BYTES_BEFORE_FLUSH = 5 * 1024 * 1024 # 5 MB + GLOBAL_MAX_BYTES_BEFORE_FLUSH = 25 * 1024 * 1024 # 25 MB FLUSH_WAIT_TIME = 5.0 @@ -50,6 +51,8 @@ def __init__( self._span_number: int = 0 self._running_size: dict[str, int] = defaultdict(lambda: 0) + self._total_running_size: int = 0 + self._capture_func = capture_func self._record_lost_func = record_lost_func self._running = True @@ -79,6 +82,8 @@ def _reset_thread_state(self) -> None: self._span_number = 0 self._running_size = defaultdict(lambda: 0) + self._total_running_size = 0 + self._running = True self._lock = threading.Lock() @@ -100,7 +105,7 @@ def _flush_loop(self) -> None: self._flush(only_pending=True) - if ( + if self._total_running_size >= self.GLOBAL_MAX_BYTES_BEFORE_FLUSH or ( time.monotonic() - self._last_full_flush >= self.FLUSH_WAIT_TIME + jitter ): @@ -138,7 +143,9 @@ def add(self, span: "SpanJSON") -> None: self._span_buffer[span["trace_id"]].append(span) self._span_number += 1 - self._running_size[span["trace_id"]] += self._estimate_size(span) + estimated_size = self._estimate_size(span) + self._running_size[span["trace_id"]] += estimated_size + self._total_running_size += estimated_size if ( size + 1 >= self.MAX_BEFORE_FLUSH @@ -148,7 +155,9 @@ def add(self, span: "SpanJSON") -> None: self._pending_flush.add(span["trace_id"]) notify = True else: - notify = False + notify = ( + self._total_running_size >= self.GLOBAL_MAX_BYTES_BEFORE_FLUSH + ) if notify: self._flush_event.set() @@ -242,6 +251,7 @@ def _flush(self, only_pending: bool = False) -> None: self._span_number -= len(self._span_buffer[bucket_id]) del self._span_buffer[bucket_id] + self._total_running_size -= self._running_size[bucket_id] del self._running_size[bucket_id] for envelope in envelopes: diff --git a/tests/tracing/test_span_batcher.py b/tests/tracing/test_span_batcher.py index 507fef184b..84910338a3 100644 --- a/tests/tracing/test_span_batcher.py +++ b/tests/tracing/test_span_batcher.py @@ -338,6 +338,81 @@ def test_weight_based_flushing_by_attribute_size( assert envelopes[0].items[0].payload.json["items"][1]["name"] == "big span" +def test_global_length_based_flushing(sentry_init, capture_items, monkeypatch): + """When a the batcher reaches GLOBAL_MAX_BYTES_BEFORE_FLUSH, all buckets will be flushed.""" + monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 3) + # set the time-based flush limit to something huge so that it doesn't + # interfere + monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000) + + sentry_init( + traces_sample_rate=1.0, + trace_lifecycle="stream", + ) + + items = capture_items("span") + + with sentry_sdk.traces.start_span(name="span"): + pass + + sentry_sdk.traces.new_trace() + with sentry_sdk.traces.start_span(name="span"): + pass + + sentry_sdk.traces.new_trace() + with sentry_sdk.traces.start_span(name="span"): + pass + + time.sleep(0.1) + + assert len(items) == 3 + assert items[0].payload["name"] == "span" + + +def test_capture_after_length_based_flushing(sentry_init, capture_items, monkeypatch): + """Spans are flushed again after a flush reduces the combined span size in bytes below the global limit.""" + monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 3) + # set the time-based flush limit to something huge so that it doesn't + # interfere + monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000) + + sentry_init( + traces_sample_rate=1.0, + trace_lifecycle="stream", + ) + + items = capture_items("span") + + with sentry_sdk.traces.start_span(name="span"): + pass + + sentry_sdk.traces.new_trace() + with sentry_sdk.traces.start_span(name="span"): + pass + + sentry_sdk.traces.new_trace() + with sentry_sdk.traces.start_span(name="span"): + pass + + time.sleep(0.1) + + with sentry_sdk.traces.start_span(name="span"): + pass + + sentry_sdk.traces.new_trace() + with sentry_sdk.traces.start_span(name="span"): + pass + + sentry_sdk.traces.new_trace() + with sentry_sdk.traces.start_span(name="span"): + pass + + time.sleep(0.1) + + assert len(items) == 6 + assert items[0].payload["name"] == "span" + + def test_bucket_recreated_after_flush(sentry_init, capture_envelopes, monkeypatch): """Spans for a trace that arrive after that trace's bucket was flushed land in a fresh bucket.""" monkeypatch.setattr(SpanBatcher, "MAX_BEFORE_FLUSH", 2) @@ -545,6 +620,8 @@ def test_span_batcher_lock_reset_in_child_after_fork(sentry_init): batcher._span_number = 1 batcher._running_size["test-trace-id"] = 42 + batcher._total_running_size = 42 + batcher._active.flag = True batcher._flush_event.set() batcher._running = False @@ -559,6 +636,7 @@ def test_span_batcher_lock_reset_in_child_after_fork(sentry_init): span_number_reset = batcher._span_number == 0 running_size_reset = len(batcher._running_size) == 0 + total_running_size_reset = batcher._total_running_size == 0 active_reset = not getattr(batcher._active, "flag", False) event_reset = not batcher._flush_event.is_set() @@ -572,6 +650,7 @@ def test_span_batcher_lock_reset_in_child_after_fork(sentry_init): and span_buffer_reset and span_number_reset and running_size_reset + and total_running_size_reset and active_reset and event_reset and running_reset From 902b89a0d508e0efe1cca82c11676206ecd563c0 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 09:11:11 +0200 Subject: [PATCH 05/11] polish tests --- tests/tracing/test_span_batcher.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/tests/tracing/test_span_batcher.py b/tests/tracing/test_span_batcher.py index 84910338a3..96cd37326b 100644 --- a/tests/tracing/test_span_batcher.py +++ b/tests/tracing/test_span_batcher.py @@ -340,7 +340,8 @@ def test_weight_based_flushing_by_attribute_size( def test_global_length_based_flushing(sentry_init, capture_items, monkeypatch): """When a the batcher reaches GLOBAL_MAX_BYTES_BEFORE_FLUSH, all buckets will be flushed.""" - monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 3) + # Limit of 2_000 is just above size of bare span. + monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 2_000) # set the time-based flush limit to something huge so that it doesn't # interfere monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000) @@ -359,19 +360,16 @@ def test_global_length_based_flushing(sentry_init, capture_items, monkeypatch): with sentry_sdk.traces.start_span(name="span"): pass - sentry_sdk.traces.new_trace() - with sentry_sdk.traces.start_span(name="span"): - pass - time.sleep(0.1) - assert len(items) == 3 + assert len(items) == 2 assert items[0].payload["name"] == "span" def test_capture_after_length_based_flushing(sentry_init, capture_items, monkeypatch): """Spans are flushed again after a flush reduces the combined span size in bytes below the global limit.""" - monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 3) + # Limit of 2_000 is just above size of bare span. + monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 2_000) # set the time-based flush limit to something huge so that it doesn't # interfere monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000) @@ -390,10 +388,6 @@ def test_capture_after_length_based_flushing(sentry_init, capture_items, monkeyp with sentry_sdk.traces.start_span(name="span"): pass - sentry_sdk.traces.new_trace() - with sentry_sdk.traces.start_span(name="span"): - pass - time.sleep(0.1) with sentry_sdk.traces.start_span(name="span"): @@ -403,13 +397,9 @@ def test_capture_after_length_based_flushing(sentry_init, capture_items, monkeyp with sentry_sdk.traces.start_span(name="span"): pass - sentry_sdk.traces.new_trace() - with sentry_sdk.traces.start_span(name="span"): - pass - time.sleep(0.1) - assert len(items) == 6 + assert len(items) == 4 assert items[0].payload["name"] == "span" From 2f6611fb81025f7deac3096585dd02c179b7e73d Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 09:12:21 +0200 Subject: [PATCH 06/11] improve comments --- tests/tracing/test_span_batcher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tracing/test_span_batcher.py b/tests/tracing/test_span_batcher.py index 96cd37326b..ae02667792 100644 --- a/tests/tracing/test_span_batcher.py +++ b/tests/tracing/test_span_batcher.py @@ -340,7 +340,7 @@ def test_weight_based_flushing_by_attribute_size( def test_global_length_based_flushing(sentry_init, capture_items, monkeypatch): """When a the batcher reaches GLOBAL_MAX_BYTES_BEFORE_FLUSH, all buckets will be flushed.""" - # Limit of 2_000 is just above size of bare span. + # Limit of 2_000 is just above the size of a bare span. monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 2_000) # set the time-based flush limit to something huge so that it doesn't # interfere @@ -368,7 +368,7 @@ def test_global_length_based_flushing(sentry_init, capture_items, monkeypatch): def test_capture_after_length_based_flushing(sentry_init, capture_items, monkeypatch): """Spans are flushed again after a flush reduces the combined span size in bytes below the global limit.""" - # Limit of 2_000 is just above size of bare span. + # Limit of 2_000 is just above the size of a bare span. monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 2_000) # set the time-based flush limit to something huge so that it doesn't # interfere From faf5684ebc7039aeaf8a2a7d208c360524c38a52 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 09:30:39 +0200 Subject: [PATCH 07/11] do not index if global limit is reached --- sentry_sdk/_span_batcher.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/_span_batcher.py b/sentry_sdk/_span_batcher.py index 5f2e8576b9..27c9852c61 100644 --- a/sentry_sdk/_span_batcher.py +++ b/sentry_sdk/_span_batcher.py @@ -123,10 +123,9 @@ def add(self, span: "SpanJSON") -> None: return None with self._lock: - size = len(self._span_buffer[span["trace_id"]]) if ( - size >= self.MAX_BEFORE_DROP - or self._span_number >= self.GLOBAL_MAX_BEFORE_DROP + self._span_number >= self.GLOBAL_MAX_BEFORE_DROP + or len(self._span_buffer[span["trace_id"]]) >= self.MAX_BEFORE_DROP ): self._record_lost_func( reason="queue_overflow", @@ -141,7 +140,8 @@ def add(self, span: "SpanJSON") -> None: self._running_size[span["trace_id"]] += self._estimate_size(span) if ( - size + 1 >= self.MAX_BEFORE_FLUSH + len(self._span_buffer[span["trace_id"]]) + 1 + >= self.MAX_BEFORE_FLUSH or self._running_size[span["trace_id"]] >= self.MAX_BYTES_BEFORE_FLUSH ): From 5a295fa8507c4b0494ee0cf25d984ad4894ee616 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 09:32:55 +0200 Subject: [PATCH 08/11] fix off by one error --- sentry_sdk/_span_batcher.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sentry_sdk/_span_batcher.py b/sentry_sdk/_span_batcher.py index 27c9852c61..c1c40044ee 100644 --- a/sentry_sdk/_span_batcher.py +++ b/sentry_sdk/_span_batcher.py @@ -140,8 +140,7 @@ def add(self, span: "SpanJSON") -> None: self._running_size[span["trace_id"]] += self._estimate_size(span) if ( - len(self._span_buffer[span["trace_id"]]) + 1 - >= self.MAX_BEFORE_FLUSH + len(self._span_buffer[span["trace_id"]]) >= self.MAX_BEFORE_FLUSH or self._running_size[span["trace_id"]] >= self.MAX_BYTES_BEFORE_FLUSH ): From 9cd210256009b0b8d4c00bea797b36fe648e21bf Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 09:40:13 +0200 Subject: [PATCH 09/11] fix test assertion --- tests/tracing/test_span_batcher.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/tracing/test_span_batcher.py b/tests/tracing/test_span_batcher.py index ae02667792..6228b02f66 100644 --- a/tests/tracing/test_span_batcher.py +++ b/tests/tracing/test_span_batcher.py @@ -366,8 +366,10 @@ def test_global_length_based_flushing(sentry_init, capture_items, monkeypatch): assert items[0].payload["name"] == "span" -def test_capture_after_length_based_flushing(sentry_init, capture_items, monkeypatch): - """Spans are flushed again after a flush reduces the combined span size in bytes below the global limit.""" +def test_size_total_reset_after_length_based_flushing( + sentry_init, capture_items, monkeypatch +): + """Span is not flushed after a flush reduces the combined span size in bytes below the global limit.""" # Limit of 2_000 is just above the size of a bare span. monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 2_000) # set the time-based flush limit to something huge so that it doesn't @@ -390,16 +392,12 @@ def test_capture_after_length_based_flushing(sentry_init, capture_items, monkeyp time.sleep(0.1) - with sentry_sdk.traces.start_span(name="span"): - pass - - sentry_sdk.traces.new_trace() with sentry_sdk.traces.start_span(name="span"): pass time.sleep(0.1) - assert len(items) == 4 + assert len(items) == 2 assert items[0].payload["name"] == "span" From 548409d2f85504bdb61c22177d5a32644f0e47e5 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 09:44:14 +0200 Subject: [PATCH 10/11] fix test --- tests/tracing/test_span_batcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tracing/test_span_batcher.py b/tests/tracing/test_span_batcher.py index 6228b02f66..507e8a85da 100644 --- a/tests/tracing/test_span_batcher.py +++ b/tests/tracing/test_span_batcher.py @@ -339,7 +339,7 @@ def test_weight_based_flushing_by_attribute_size( def test_global_length_based_flushing(sentry_init, capture_items, monkeypatch): - """When a the batcher reaches GLOBAL_MAX_BYTES_BEFORE_FLUSH, all buckets will be flushed.""" + """When the batcher reaches GLOBAL_MAX_BYTES_BEFORE_FLUSH, all buckets will be flushed.""" # Limit of 2_000 is just above the size of a bare span. monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 2_000) # set the time-based flush limit to something huge so that it doesn't From 75fc7df0221323515058396586ee68757880ab25 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 10 Aug 2026 10:50:29 +0200 Subject: [PATCH 11/11] rename test --- tests/tracing/test_span_batcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tracing/test_span_batcher.py b/tests/tracing/test_span_batcher.py index 507e8a85da..fa34ab26e1 100644 --- a/tests/tracing/test_span_batcher.py +++ b/tests/tracing/test_span_batcher.py @@ -366,7 +366,7 @@ def test_global_length_based_flushing(sentry_init, capture_items, monkeypatch): assert items[0].payload["name"] == "span" -def test_size_total_reset_after_length_based_flushing( +def test_total_size_reset_after_length_based_flushing( sentry_init, capture_items, monkeypatch ): """Span is not flushed after a flush reduces the combined span size in bytes below the global limit."""