Skip to content

Commit 1fea08e

Browse files
ref: Revert global span batcher limits (#7168)
1 parent 8e18c44 commit 1fea08e

2 files changed

Lines changed: 9 additions & 253 deletions

File tree

sentry_sdk/_span_batcher.py

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,12 @@ class SpanBatcher(Batcher["SpanJSON"]):
2121
# MAX_BEFORE_FLUSH should be lower than MAX_BEFORE_DROP, so that there is
2222
# a bit of a buffer for spans that appear between the trigger to flush
2323
# and actually flushing the buffer.
24+
#
25+
# The max limits are all per trace (per bucket).
2426
MAX_ENVELOPE_SIZE = 1000 # spans
25-
2627
MAX_BEFORE_FLUSH = 1000
27-
GLOBAL_MAX_BEFORE_FLUSH = 5_000
28-
2928
MAX_BEFORE_DROP = 2000
30-
GLOBAL_MAX_BEFORE_DROP = 10_000
31-
3229
MAX_BYTES_BEFORE_FLUSH = 5 * 1024 * 1024 # 5 MB
33-
GLOBAL_MAX_BYTES_BEFORE_FLUSH = 25 * 1024 * 1024 # 25 MB
3430

3531
FLUSH_WAIT_TIME = 5.0
3632

@@ -48,11 +44,7 @@ def __init__(
4844
# envelope.
4945
# trace_id -> span buffer
5046
self._span_buffer: dict[str, list["SpanJSON"]] = defaultdict(list)
51-
self._span_number: int = 0
52-
5347
self._running_size: dict[str, int] = defaultdict(lambda: 0)
54-
self._total_running_size: int = 0
55-
5648
self._capture_func = capture_func
5749
self._record_lost_func = record_lost_func
5850
self._running = True
@@ -79,11 +71,7 @@ def _reset_in_child() -> None:
7971

8072
def _reset_thread_state(self) -> None:
8173
self._span_buffer = defaultdict(list)
82-
self._span_number = 0
83-
8474
self._running_size = defaultdict(lambda: 0)
85-
self._total_running_size = 0
86-
8775
self._running = True
8876

8977
self._lock = threading.Lock()
@@ -106,12 +94,8 @@ def _flush_loop(self) -> None:
10694
self._flush(only_pending=True)
10795

10896
if (
109-
self._span_number >= self.GLOBAL_MAX_BEFORE_FLUSH
110-
or self._total_running_size >= self.GLOBAL_MAX_BYTES_BEFORE_FLUSH
111-
or (
112-
time.monotonic() - self._last_full_flush
113-
>= self.FLUSH_WAIT_TIME + jitter
114-
)
97+
time.monotonic() - self._last_full_flush
98+
>= self.FLUSH_WAIT_TIME + jitter
11599
):
116100
self._flush()
117101
self._last_full_flush = time.monotonic()
@@ -132,10 +116,8 @@ def add(self, span: "SpanJSON") -> None:
132116
return None
133117

134118
with self._lock:
135-
if (
136-
self._span_number >= self.GLOBAL_MAX_BEFORE_DROP
137-
or len(self._span_buffer[span["trace_id"]]) >= self.MAX_BEFORE_DROP
138-
):
119+
size = len(self._span_buffer[span["trace_id"]])
120+
if size >= self.MAX_BEFORE_DROP:
139121
self._record_lost_func(
140122
reason="queue_overflow",
141123
data_category="span",
@@ -144,25 +126,17 @@ def add(self, span: "SpanJSON") -> None:
144126
return None
145127

146128
self._span_buffer[span["trace_id"]].append(span)
147-
self._span_number += 1
148-
149-
estimated_size = self._estimate_size(span)
150-
self._running_size[span["trace_id"]] += estimated_size
151-
self._total_running_size += estimated_size
129+
self._running_size[span["trace_id"]] += self._estimate_size(span)
152130

153131
if (
154-
len(self._span_buffer[span["trace_id"]]) >= self.MAX_BEFORE_FLUSH
132+
size + 1 >= self.MAX_BEFORE_FLUSH
155133
or self._running_size[span["trace_id"]]
156134
>= self.MAX_BYTES_BEFORE_FLUSH
157135
):
158136
self._pending_flush.add(span["trace_id"])
159137
notify = True
160138
else:
161-
notify = (
162-
self._span_number >= self.GLOBAL_MAX_BEFORE_FLUSH
163-
or self._total_running_size
164-
>= self.GLOBAL_MAX_BYTES_BEFORE_FLUSH
165-
)
139+
notify = False
166140

167141
if notify:
168142
self._flush_event.set()
@@ -253,10 +227,7 @@ def _flush(self, only_pending: bool = False) -> None:
253227

254228
envelopes.append(envelope)
255229

256-
self._span_number -= len(self._span_buffer[bucket_id])
257230
del self._span_buffer[bucket_id]
258-
259-
self._total_running_size -= self._running_size[bucket_id]
260231
del self._running_size[bucket_id]
261232

262233
for envelope in envelopes:

tests/tracing/test_span_batcher.py

Lines changed: 0 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -171,88 +171,6 @@ def test_drop_isolated_per_bucket(
171171
assert record_lost_event_calls.count(("queue_overflow", "span", None, 1)) == 1
172172

173173

174-
def test_drop_after_global_max_reached(
175-
sentry_init, capture_envelopes, capture_record_lost_event_calls, monkeypatch
176-
):
177-
"""New spans are dropped if the buffer reaches GLOBAL_MAX_BEFORE_DROP spans."""
178-
monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BEFORE_DROP", 2)
179-
# set the time-based flush limit to something huge so that we're not flushing
180-
# prematurely
181-
monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000)
182-
183-
sentry_init(
184-
traces_sample_rate=1.0,
185-
trace_lifecycle="stream",
186-
)
187-
188-
envelopes = capture_envelopes()
189-
record_lost_event_calls = capture_record_lost_event_calls()
190-
191-
with sentry_sdk.traces.start_span(name="span 1"):
192-
pass
193-
with sentry_sdk.traces.start_span(name="span 2"):
194-
pass
195-
with sentry_sdk.traces.start_span(name="span 3"):
196-
pass
197-
198-
sentry_sdk.traces.new_trace()
199-
with sentry_sdk.traces.start_span(name="span 4"):
200-
pass
201-
202-
sentry_sdk.flush()
203-
204-
assert len(envelopes) == 1
205-
206-
assert len(envelopes[0].items[0].payload.json["items"]) == 2
207-
assert envelopes[0].items[0].payload.json["items"][0]["name"] == "span 1"
208-
assert envelopes[0].items[0].payload.json["items"][1]["name"] == "span 2"
209-
210-
assert record_lost_event_calls.count(("queue_overflow", "span", None, 1)) == 2
211-
212-
213-
def test_capture_after_flush_with_global_limit(
214-
sentry_init, capture_envelopes, monkeypatch
215-
):
216-
"""New spans are captured again after a flush reduces the span number below the global limit."""
217-
monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BEFORE_DROP", 2)
218-
# set the time-based flush limit to something huge so that we're not flushing
219-
# prematurely
220-
monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000)
221-
222-
sentry_init(
223-
traces_sample_rate=1.0,
224-
trace_lifecycle="stream",
225-
)
226-
227-
envelopes = capture_envelopes()
228-
229-
with sentry_sdk.traces.start_span(name="span 1"):
230-
pass
231-
with sentry_sdk.traces.start_span(name="span 2"):
232-
pass
233-
234-
sentry_sdk.traces.new_trace()
235-
with sentry_sdk.traces.start_span(name="span 3"):
236-
pass
237-
238-
sentry_sdk.flush()
239-
240-
# The span is captured even though a span was dropped in the same trace.
241-
with sentry_sdk.traces.start_span(name="span 4"):
242-
pass
243-
244-
sentry_sdk.flush()
245-
246-
assert len(envelopes) == 2
247-
248-
assert len(envelopes[0].items[0].payload.json["items"]) == 2
249-
assert envelopes[0].items[0].payload.json["items"][0]["name"] == "span 1"
250-
assert envelopes[0].items[0].payload.json["items"][1]["name"] == "span 2"
251-
252-
assert len(envelopes[1].items[0].payload.json["items"]) == 1
253-
assert envelopes[1].items[0].payload.json["items"][0]["name"] == "span 4"
254-
255-
256174
def test_length_based_flushing(sentry_init, capture_items, monkeypatch):
257175
"""A flush event is triggered when a bucket contains MAX_BEFORE_FLUSH spans."""
258176
monkeypatch.setattr(SpanBatcher, "MAX_BEFORE_FLUSH", 1)
@@ -338,130 +256,6 @@ def test_weight_based_flushing_by_attribute_size(
338256
assert envelopes[0].items[0].payload.json["items"][1]["name"] == "big span"
339257

340258

341-
def test_global_length_based_flushing(sentry_init, capture_items, monkeypatch):
342-
"""A flush event is triggered when the batcher contains GLOBAL_MAX_BEFORE_FLUSH spans."""
343-
monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BEFORE_FLUSH", 2)
344-
# set the time-based flush limit to something huge so that we're not hitting
345-
# it since we want to test GLOBAL_MAX_BEFORE_FLUSH instead
346-
monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000)
347-
348-
sentry_init(
349-
traces_sample_rate=1.0,
350-
trace_lifecycle="stream",
351-
)
352-
353-
items = capture_items("span")
354-
355-
with sentry_sdk.traces.start_span(name="span"):
356-
pass
357-
358-
sentry_sdk.traces.new_trace()
359-
with sentry_sdk.traces.start_span(name="span 2"):
360-
pass
361-
362-
time.sleep(0.1)
363-
364-
assert len(items) == 2
365-
assert items[0].payload["name"] == "span"
366-
367-
368-
def test_span_number_reset_after_length_based_flushing(
369-
sentry_init, capture_items, monkeypatch
370-
):
371-
"""Span is not flushed after a flush reduces the number of spans in the batcher below the global limit."""
372-
monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BEFORE_FLUSH", 2)
373-
# set the time-based flush limit to something huge so that we're not hitting
374-
# it since we want to test GLOBAL_MAX_BYTES_BEFORE_FLUSH instead
375-
monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000)
376-
377-
sentry_init(
378-
traces_sample_rate=1.0,
379-
trace_lifecycle="stream",
380-
)
381-
382-
items = capture_items("span")
383-
384-
with sentry_sdk.traces.start_span(name="span"):
385-
pass
386-
387-
sentry_sdk.traces.new_trace()
388-
with sentry_sdk.traces.start_span(name="span"):
389-
pass
390-
391-
time.sleep(0.1)
392-
393-
with sentry_sdk.traces.start_span(name="span"):
394-
pass
395-
396-
time.sleep(0.1)
397-
398-
assert len(items) == 2
399-
assert items[0].payload["name"] == "span"
400-
401-
402-
def test_global_weight_based_flushing(sentry_init, capture_items, monkeypatch):
403-
"""When the batcher reaches GLOBAL_MAX_BYTES_BEFORE_FLUSH, all buckets will be flushed."""
404-
# Limit of 2_000 is just above the size of a bare span.
405-
monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 2_000)
406-
# set the time-based flush limit to something huge so that it doesn't
407-
# interfere
408-
monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000)
409-
410-
sentry_init(
411-
traces_sample_rate=1.0,
412-
trace_lifecycle="stream",
413-
)
414-
415-
items = capture_items("span")
416-
417-
with sentry_sdk.traces.start_span(name="span"):
418-
pass
419-
420-
sentry_sdk.traces.new_trace()
421-
with sentry_sdk.traces.start_span(name="span"):
422-
pass
423-
424-
time.sleep(0.1)
425-
426-
assert len(items) == 2
427-
assert items[0].payload["name"] == "span"
428-
429-
430-
def test_total_size_reset_after_weight_based_flushing(
431-
sentry_init, capture_items, monkeypatch
432-
):
433-
"""Span is not flushed after a flush reduces the combined span size in bytes below the global limit."""
434-
# Limit of 2_000 is just above the size of a bare span.
435-
monkeypatch.setattr(SpanBatcher, "GLOBAL_MAX_BYTES_BEFORE_FLUSH", 2_000)
436-
# set the time-based flush limit to something huge so that it doesn't
437-
# interfere
438-
monkeypatch.setattr(SpanBatcher, "FLUSH_WAIT_TIME", 100000)
439-
440-
sentry_init(
441-
traces_sample_rate=1.0,
442-
trace_lifecycle="stream",
443-
)
444-
445-
items = capture_items("span")
446-
447-
with sentry_sdk.traces.start_span(name="span"):
448-
pass
449-
450-
sentry_sdk.traces.new_trace()
451-
with sentry_sdk.traces.start_span(name="span"):
452-
pass
453-
454-
time.sleep(0.1)
455-
456-
with sentry_sdk.traces.start_span(name="span"):
457-
pass
458-
459-
time.sleep(0.1)
460-
461-
assert len(items) == 2
462-
assert items[0].payload["name"] == "span"
463-
464-
465259
def test_bucket_recreated_after_flush(sentry_init, capture_envelopes, monkeypatch):
466260
"""Spans for a trace that arrive after that trace's bucket was flushed land in a fresh bucket."""
467261
monkeypatch.setattr(SpanBatcher, "MAX_BEFORE_FLUSH", 2)
@@ -666,11 +460,7 @@ def test_span_batcher_lock_reset_in_child_after_fork(sentry_init):
666460
original_lock.acquire()
667461

668462
batcher._span_buffer["test-trace-id"].append(object())
669-
batcher._span_number = 1
670-
671463
batcher._running_size["test-trace-id"] = 42
672-
batcher._total_running_size = 42
673-
674464
batcher._active.flag = True
675465
batcher._flush_event.set()
676466
batcher._running = False
@@ -682,10 +472,7 @@ def test_span_batcher_lock_reset_in_child_after_fork(sentry_init):
682472

683473
flusher_reset = batcher._flusher is None and batcher._flusher_pid is None
684474
span_buffer_reset = len(batcher._span_buffer) == 0
685-
span_number_reset = batcher._span_number == 0
686-
687475
running_size_reset = len(batcher._running_size) == 0
688-
total_running_size_reset = batcher._total_running_size == 0
689476

690477
active_reset = not getattr(batcher._active, "flag", False)
691478
event_reset = not batcher._flush_event.is_set()
@@ -697,9 +484,7 @@ def test_span_batcher_lock_reset_in_child_after_fork(sentry_init):
697484
and unheld
698485
and flusher_reset
699486
and span_buffer_reset
700-
and span_number_reset
701487
and running_size_reset
702-
and total_running_size_reset
703488
and active_reset
704489
and event_reset
705490
and running_reset

0 commit comments

Comments
 (0)