Skip to content

Commit 520158d

Browse files
authored
fix: Better memory usage for tracing (getsentry#431)
* fix: Better memory usage for tracing Fix getsentry#430 * fix: Remove useless gc.collect * Revert "fix: Remove useless gc.collect" This reverts commit 897b683. * doc: Add comment to gc.collect * fix: Do not trim at all
1 parent 37830aa commit 520158d

File tree

3 files changed

+58
-12
lines changed

3 files changed

+58
-12
lines changed

sentry_sdk/hub.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,9 @@ def start_span(
479479
sample_rate = client and client.options["traces_sample_rate"] or 0
480480
span.sampled = random.random() < sample_rate
481481

482+
if span.sampled:
483+
span.init_finished_spans()
484+
482485
return span
483486

484487
def finish_span(
@@ -517,7 +520,9 @@ def finish_span(
517520
"contexts": {"trace": span.get_trace_context()},
518521
"timestamp": span.timestamp,
519522
"start_timestamp": span.start_timestamp,
520-
"spans": [s.to_json() for s in span._finished_spans if s is not span],
523+
"spans": [
524+
s.to_json() for s in (span._finished_spans or ()) if s is not span
525+
],
521526
}
522527
)
523528

sentry_sdk/tracing.py

+18-11
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66

77
from sentry_sdk.utils import capture_internal_exceptions, concat_strings
88
from sentry_sdk._compat import PY2
9+
from sentry_sdk._types import MYPY
910

1011
if PY2:
1112
from collections import Mapping
1213
else:
1314
from collections.abc import Mapping
1415

15-
if False:
16+
if MYPY:
1617
import typing
1718

1819
from typing import Optional
@@ -76,15 +77,16 @@ class Span(object):
7677

7778
def __init__(
7879
self,
79-
trace_id=None,
80-
span_id=None,
81-
parent_span_id=None,
82-
same_process_as_parent=True,
83-
sampled=None,
84-
transaction=None,
85-
op=None,
86-
description=None,
80+
trace_id=None, # type: Optional[str]
81+
span_id=None, # type: Optional[str]
82+
parent_span_id=None, # type: Optional[str]
83+
same_process_as_parent=True, # type: bool
84+
sampled=None, # type: Optional[bool]
85+
transaction=None, # type: Optional[str]
86+
op=None, # type: Optional[str]
87+
description=None, # type: Optional[str]
8788
):
89+
# type: (...) -> None
8890
self.trace_id = trace_id or uuid.uuid4().hex
8991
self.span_id = span_id or uuid.uuid4().hex[16:]
9092
self.parent_span_id = parent_span_id
@@ -95,12 +97,16 @@ def __init__(
9597
self.description = description
9698
self._tags = {} # type: Dict[str, str]
9799
self._data = {} # type: Dict[str, Any]
98-
self._finished_spans = [] # type: List[Span]
100+
self._finished_spans = None # type: Optional[List[Span]]
99101
self.start_timestamp = datetime.now()
100102

101103
#: End timestamp of span
102104
self.timestamp = None
103105

106+
def init_finished_spans(self):
107+
if self._finished_spans is None:
108+
self._finished_spans = []
109+
104110
def __repr__(self):
105111
return (
106112
"<%s(transaction=%r, trace_id=%r, span_id=%r, parent_span_id=%r, sampled=%r)>"
@@ -184,7 +190,8 @@ def set_data(self, key, value):
184190

185191
def finish(self):
186192
self.timestamp = datetime.now()
187-
self._finished_spans.append(self)
193+
if self._finished_spans is not None:
194+
self._finished_spans.append(self)
188195

189196
def to_json(self):
190197
return {

tests/test_tracing.py

+34
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import weakref
2+
import gc
3+
14
import pytest
25

36
from sentry_sdk import Hub, capture_message
@@ -93,3 +96,34 @@ def test_sampling_decided_only_for_transactions(sentry_init, capture_events):
9396

9497
with Hub.current.span() as span:
9598
assert span.sampled is None
99+
100+
101+
@pytest.mark.parametrize(
102+
"args,expected_refcount",
103+
[({"traces_sample_rate": 1.0}, 100), ({"traces_sample_rate": 0.0}, 0)],
104+
)
105+
def test_memory_usage(sentry_init, capture_events, args, expected_refcount):
106+
sentry_init(**args)
107+
108+
references = weakref.WeakSet()
109+
110+
with Hub.current.span(transaction="hi"):
111+
for i in range(100):
112+
with Hub.current.span(
113+
op="helloworld", description="hi {}".format(i)
114+
) as span:
115+
116+
def foo():
117+
pass
118+
119+
references.add(foo)
120+
span.set_tag("foo", foo)
121+
pass
122+
123+
del foo
124+
del span
125+
126+
# required only for pypy (cpython frees immediately)
127+
gc.collect()
128+
129+
assert len(references) == expected_refcount

0 commit comments

Comments
 (0)