Skip to content

Commit

Permalink
fix: Discard entire span tree if too large
Browse files Browse the repository at this point in the history
  • Loading branch information
untitaker committed Sep 19, 2019
1 parent da4a21c commit 3a27d87
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 14 deletions.
1 change: 1 addition & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def __init__(
# DO NOT ENABLE THIS RIGHT NOW UNLESS YOU WANT TO EXCEED YOUR EVENT QUOTA IMMEDIATELY
traces_sample_rate=0.0, # type: float
traceparent_v2=False, # type: bool
_experiments={}, # type: Dict[str, Any]
):
# type: (...) -> None
pass
Expand Down
5 changes: 4 additions & 1 deletion sentry_sdk/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,10 @@ def start_span(
span.sampled = random.random() < sample_rate

if span.sampled:
span.init_finished_spans()
max_spans = (
client and client.options["_experiments"].get("max_spans") or 1000
)
span.init_finished_spans(maxlen=max_spans)

return span

Expand Down
48 changes: 35 additions & 13 deletions sentry_sdk/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import uuid
import contextlib

from collections import deque
from datetime import datetime

import sentry_sdk
Expand All @@ -24,7 +23,7 @@
from typing import Dict
from typing import List
from typing import Tuple
from typing import Deque
from typing import Iterator

_traceparent_header_format_re = re.compile(
"^[ \t]*" # whitespace
Expand Down Expand Up @@ -66,6 +65,30 @@ def __iter__(self):
yield k[len(self.prefix) :]


class _SpanRecorder(object):
__slots__ = ("maxlen", "finished_spans")

def __init__(self):
# type: () -> None
self.maxlen = 1000
self.finished_spans = None # type: Optional[List[Span]]

def append(self, span):
# type: (Span) -> None
if self.finished_spans is not None:
if len(self.finished_spans) >= self.maxlen:
# If the span tree grows too large we decided it's better to
# discard all spans instead of trying to apply some expensive
# trimming.
self.finished_spans = None
else:
self.finished_spans.append(span)

def __iter__(self):
# type: () -> Iterator[Span]
return iter(self.finished_spans or ())


class Span(object):
__slots__ = (
"trace_id",
Expand All @@ -80,7 +103,7 @@ class Span(object):
"timestamp",
"_tags",
"_data",
"_finished_spans",
"_span_recorder",
"hub",
"_context_manager_state",
)
Expand Down Expand Up @@ -109,16 +132,18 @@ def __init__(
self.hub = hub
self._tags = {} # type: Dict[str, str]
self._data = {} # type: Dict[str, Any]
self._finished_spans = None # type: Optional[Deque[Span]]
self._span_recorder = _SpanRecorder() # type: _SpanRecorder
self.start_timestamp = datetime.now()

#: End timestamp of span
self.timestamp = None # type: Optional[datetime]

def init_finished_spans(self, maxlen):
# type: () -> None
if self._finished_spans is None:
self._finished_spans = deque(maxlen=maxlen)
# type: (int) -> None
if self._span_recorder.finished_spans is None:
self._span_recorder.finished_spans = []

self._span_recorder.maxlen = maxlen

def __repr__(self):
# type: () -> str
Expand Down Expand Up @@ -164,7 +189,7 @@ def new_span(self, **kwargs):
sampled=self.sampled,
**kwargs
)
rv._finished_spans = self._finished_spans
rv._span_recorder = self._span_recorder
return rv

@classmethod
Expand Down Expand Up @@ -254,8 +279,7 @@ def finish(self, hub=None):

self.timestamp = datetime.now()

if self._finished_spans is not None:
self._finished_spans.append(self)
self._span_recorder.append(self)

_maybe_create_breadcrumbs_from_span(hub, self)

Expand Down Expand Up @@ -286,9 +310,7 @@ def finish(self, hub=None):
"contexts": {"trace": self.get_trace_context()},
"timestamp": self.timestamp,
"start_timestamp": self.start_timestamp,
"spans": [
s.to_json() for s in (self._finished_spans or ()) if s is not self
],
"spans": [s.to_json() for s in self._span_recorder if s is not self],
}
)

Expand Down

0 comments on commit 3a27d87

Please sign in to comment.