8000 fix: Better memory usage for tracing · etherscan-io/sentry-python@614ec68 · GitHub
[go: up one dir, main page]

Skip to content

Commit 614ec68

Browse files
committed
fix: Better memory usage for tracing
Fix getsentry#430
1 parent a32f41c commit 614ec68

File tree

3 files changed

+64
-13
lines changed

3 files changed

+64
-13
lines changed

sentry_sdk/hub.py

Lines changed: 6 additions & 1 deletion
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 and client:
483+
span.init_finished_spans(client.options["max_breadcrumbs"])
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

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,25 @@
22
import uuid
33
import contextlib
44

5+
from collections import deque
56
from datetime import datetime
67

78
from sentry_sdk.utils import capture_internal_exceptions, concat_strings
89
from sentry_sdk._compat import PY2
10+
from sentry_sdk._types import MYPY
911

1012
if PY2:
1113
from collections import Mapping
1214
else:
1315
from collections.abc import Mapping
1416

15-
if False:
17+
if MYPY:
1618
import typing
1719

1820
from typing import Optional
1921
from typing import Any
2022
from typing import Dict
21-
from typing import List
23+
from typing import Deque
2224

2325
_traceparent_header_format_re = re.compile(
2426
"^[ \t]*" # whitespace
@@ -76,15 +78,16 @@ class Span(object):
7678

7779
def __init__(
7880
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,
81+
trace_id=None, # type: Optional[str]
82+
span_id=None, # type: Optional[str]
83+
parent_span_id=None, # type: Optional[str]
84+
same_process_as_parent=True, # type: bool
85+
sampled=None, # type: Optional[bool]
86+
transaction=None, # type: Optional[str]
87+
op=None, # type: Optional[str]
88+
description=None, # type: Optional[str]
8789
):
90+
# type: (...) -> None
8891
self.trace_id = trace_id or uuid.uuid4().hex
8992
self.span_id = span_id or uuid.uuid4().hex[16:]
9093
self.parent_span_id = parent_span_id
@@ -95,12 +98,16 @@ def __init__(
9598
self.description = description
9699
self._tags = {} # type: Dict[str, str]
97100
self._data = {} # type: Dict[str, Any]< 8000 /div>
98-
self._finished_spans = [] # type: List[Span]
101+
self._finished_spans = None # type: Optional[Deque[Span]]
99102
self.start_timestamp = datetime.now()
100103

101104
#: End timestamp of span
102105
self.timestamp = None
103106

107+
def init_finished_spans(self, maxlen):
108+
if self._finished_spans is None:
109+
self._finished_spans = deque(maxlen=maxlen)
110+
104111
def __repr__(self):
105112
return (
106113
"<%s(transaction=%r, trace_id=%r, span_id=%r, parent_span_id=%r, sampled=%r)>"
@@ -184,7 +191,8 @@ def set_data(self, key, value):
184191

185192
def finish(self):
186193
self.timestamp = datetime.now()
187-
self._finished_spans.append(self)
194+
if self._finished_spans is not None:
195+
self._finished_spans.append(self)
188196

189197
def to_json(self):
190198
return {

tests/test_tracing.py

Lines changed: 38 additions & 0 deletions
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,38 @@ 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+
[
104+
# Tracing is enabled, but the max amount of spans is 0
105+
({"traces_sample_rate": 1.0, "max_breadcrumbs": 0}, 0),
106+
# Tracing is enabled, but the max amount of spans is 10
107+
({"traces_sample_rate": 1.0, "max_breadcrumbs": 10}, 10),
108+
# Tracing is disabled, so max amount of spans should not matter
109+
({"traces_sample_rate": 0.0, "max_breadcrumbs": 100}, 0),
110+
],
111+
)
112+
def test_memory_usage(sentry_init, capture_events, args, expected_refcount):
113+
sentry_init(**args)
114+
115+
references = weakref.WeakSet()
116+
117+
with Hub.current.span(transaction="hi"):
118+
for i in range(100):
119+
with Hub.current.span(
120+
op="helloworld", description="hi {}".format(i)
121+
) as span:
122+
123+
def foo():
124+
pass
125+
126+
references.add(foo)
127+
span.set_tag("foo", foo)
128+
pass
129+
130+
del foo
131+
del span
132+
gc.collect()
133+
assert len(references) == expected_refcount

0 commit comments

Comments
 (0)
0