Skip to content

Commit 7b2cd99

Browse files
volodkindvvolodkindv
andauthored
fix: #6267 - addresses memory leak in SentrySpanProcessor in OTEL integration (#6271)
### Description 1. register global_event_processor in SentrySpanProcessor only once. 2. don't try to create new SentrySpanProcessor in SentryPropagator().inject() #### Issues * resolves: #6267 * resolves: PY-2417 Co-authored-by: volodkindv <volodkindv@skbkontur.ru>
1 parent d1fc138 commit 7b2cd99

4 files changed

Lines changed: 45 additions & 1 deletion

File tree

sentry_sdk/integrations/opentelemetry/propagator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def inject(
109109

110110
span_id = trace.format_span_id(current_span_context.span_id)
111111

112-
span_map = SentrySpanProcessor().otel_span_map
112+
span_map = SentrySpanProcessor.otel_span_map
113113
sentry_span = span_map.get(span_id, None)
114114
if not sentry_span:
115115
return

sentry_sdk/integrations/opentelemetry/span_processor.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class SentrySpanProcessor(SpanProcessor):
8383
# The currently open spans. Elements will be discarded after SPAN_MAX_TIME_OPEN_MINUTES
8484
open_spans: "dict[int, set[str]]" = {}
8585

86+
initialized: "bool" = False
87+
8688
def __new__(cls) -> "SentrySpanProcessor":
8789
if not hasattr(cls, "instance"):
8890
cls.instance = super().__new__(cls)
@@ -91,10 +93,15 @@ def __new__(cls) -> "SentrySpanProcessor":
9193
return cls.instance # type: ignore[misc]
9294

9395
def __init__(self) -> None:
96+
if self.initialized:
97+
return
98+
9499
@add_global_event_processor
95100
def global_event_processor(event: "Event", hint: "Hint") -> "Event":
96101
return link_trace_context_to_error_event(event, self.otel_span_map)
97102

103+
self.initialized = True
104+
98105
def _prune_old_spans(self: "SentrySpanProcessor") -> None:
99106
"""
100107
Prune spans that have been open for too long.

tests/integrations/opentelemetry/test_propagator.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
from sentry_sdk.integrations.opentelemetry.integration import (
2+
OpenTelemetryIntegration,
3+
)
4+
from opentelemetry import trace
5+
from sentry_sdk.scope import global_event_processors
6+
7+
18
import pytest
29

310
from unittest import mock
@@ -298,3 +305,21 @@ def test_inject_sentry_span_baggage():
298305
"baggage",
299306
baggage.serialize(),
300307
)
308+
309+
310+
def test_inject_no_memory_leak():
311+
312+
OpenTelemetryIntegration.setup_once()
313+
314+
tracer = trace.get_tracer(__name__)
315+
propagator = SentryPropagator()
316+
317+
cnt_before = len(global_event_processors)
318+
319+
with tracer.start_as_current_span("bar") as new_span:
320+
context = set_span_in_context(new_span)
321+
carrier = "any_carrier"
322+
propagator.inject(carrier, context)
323+
324+
cnt_after = len(global_event_processors)
325+
assert cnt_after == cnt_before

tests/integrations/opentelemetry/test_span_processor.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from sentry_sdk.scope import global_event_processors
12
import time
23
from datetime import datetime, timezone
34
from unittest import mock
@@ -621,3 +622,14 @@ def test_pruning_old_spans_on_end():
621622
span_processor.on_end(otel_span)
622623
assert sorted(list(span_processor.otel_span_map.keys())) == ["111111111abcdef"]
623624
assert sorted(list(span_processor.open_spans.values())) == [{"111111111abcdef"}]
625+
626+
627+
def test_no_memory_leak():
628+
span_processor_1 = SentrySpanProcessor()
629+
cnt_before = len(global_event_processors)
630+
631+
span_processor_2 = SentrySpanProcessor()
632+
633+
cnt_after = len(global_event_processors)
634+
assert span_processor_1 is span_processor_2
635+
assert cnt_before == cnt_after

0 commit comments

Comments
 (0)