8000 fix(celery): Fix RecursionError (#381) · etherscan-io/sentry-python@cdad688 · GitHub
[go: up one dir, main page]

Skip to content

Commit cdad688

Browse files
authored
fix(celery): Fix RecursionError (getsentry#381)
* fix(celery): Fix RecursionError * fix: Add doc comment
1 parent 041c2fa commit cdad688

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

sentry_sdk/integrations/celery.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,18 @@ def setup_once():
3333
old_build_tracer = trace.build_tracer
3434

3535
def sentry_build_tracer(name, task, *args, **kwargs):
36-
# Need to patch both methods because older celery sometimes
37-
# short-circuits to task.run if it thinks it's safe.
38-
task.__call__ = _wrap_task_call(task, task.__call__)
39-
task.run = _wrap_task_call(task, task.run)
40-
task.apply_async = _wrap_apply_async(task, task.apply_async)
36+
if not getattr(task, "_sentry_is_patched", False):
37+
# Need to patch both methods because older celery sometimes
38+
# short-circuits to task.run if it thinks it's safe.
39+
task.__call__ = _wrap_task_call(task, task.__call__)
40+
task.run = _wrap_task_call(task, task.run)
41+
task.apply_async = _wrap_apply_async(task, task.apply_async)
42+
43+
# `build_tracer` is apparently called for every task
44+
# invocation. Can't wrap every celery task for every invocation
45+
# or we will get infinitely nested wrapper functions.
46+
task._sentry_is_patched = True
47+
4148
return _wrap_tracer(task, old_build_tracer(name, task, *args, **kwargs))
4249

4350
trace.build_tracer = sentry_build_tracer

tests/integrations/celery/test_celery.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def dummy_task(x, y):
7171
invocation(dummy_task, 1, 0)
7272

7373
event, = events
74+
7475
assert event["contexts"]["trace"]["trace_id"] == span_context.trace_id
7576
assert event["contexts"]["trace"]["span_id"] != span_context.span_id
7677
assert event["transaction"] == "dummy_task"
@@ -84,6 +85,31 @@ def dummy_task(x, y):
8485
assert exception["stacktrace"]["frames"][0]["vars"]["foo"] == "42"
8586

8687

88+
def test_no_stackoverflows(celery):
89+
"""We used to have a bug in the Celery integration where its monkeypatching
90+
was repeated for every task invocation, leading to stackoverflows.
91+
92+
See https://github.com/getsentry/sentry-python/issues/265
93+
"""
94+
95+
results = []
96+
97+
@celery.task(name="dummy_task")
98+
def dummy_task():
99+
with configure_scope() as scope:
100+
scope.set_tag("foo", "bar")
101+
102+
results.append(42)
103+
104+
for _ in range(10000):
105+
dummy_task.delay()
106+
107+
assert results == [42] * 10000
108+
109+
with configure_scope() as scope:
110+
assert not scope._tags
111+
112+
87113
def test_simple_no_propagation(capture_events, init_celery):
88114
celery = init_celery(propagate_traces=False)
89115
events = capture_events()

0 commit comments

Comments
 (0)
0