Skip to content

Commit 97fb86c

Browse files
committed
fix status overwrite error for control flow exceptions. update tests to test order of spans generated since that's stable
1 parent 7d33b81 commit 97fb86c

2 files changed

Lines changed: 107 additions & 32 deletions

File tree

sentry_sdk/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@
3232
except ImportError:
3333
AIOHttpHttpException = None
3434

35+
try:
36+
from huey.exceptions import CancelExecution, RetryTask, TaskLockedException
37+
38+
HueyControlFlowExceptions = (CancelExecution, RetryTask, TaskLockedException)
39+
except ImportError:
40+
HueyControlFlowExceptions = None
41+
3542
from typing import TYPE_CHECKING
3643

3744
import sentry_sdk
@@ -1994,6 +2001,12 @@ def should_be_treated_as_error(ty: "Any", value: "Any") -> bool:
19942001
if AIOHttpHttpException and isinstance(value, AIOHttpHttpException):
19952002
return False
19962003

2004+
# Huey also has exceptions that are raised for control flow reasons, not
2005+
# because there's an actual error. This check, similar to the aiohttp one above,
2006+
# is to prevent accidentally overwriting a status of "ok" with "error"
2007+
if HueyControlFlowExceptions and isinstance(value, HueyControlFlowExceptions):
2008+
return False
2009+
19972010
return True
19982011

19992012

tests/integrations/huey/test_huey.py

Lines changed: 94 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pytest
44
from huey import __version__ as HUEY_VERSION
55
from huey.api import MemoryHuey, Result
6-
from huey.exceptions import RetryTask
6+
from huey.exceptions import CancelExecution, RetryTask
77

88
import sentry_sdk
99
from sentry_sdk import start_transaction
@@ -93,13 +93,12 @@ def division(a, b):
9393
sentry_sdk.get_client().flush()
9494

9595
payloads = [i.payload for i in items]
96-
(execute_span,) = [
97-
# Searching for this span specifically because this is what has the raised exception
98-
p
99-
for p in payloads
100-
if p["attributes"]["sentry.op"] == OP.QUEUE_TASK_HUEY
101-
]
96+
assert len(payloads) == 2
97+
enqueue_span, execute_span = payloads
98+
99+
assert enqueue_span["attributes"]["sentry.op"] == OP.QUEUE_SUBMIT_HUEY
102100
assert execute_span["is_segment"]
101+
assert execute_span["attributes"]["sentry.op"] == OP.QUEUE_TASK_HUEY
103102
assert execute_span["name"] == "division"
104103
assert execute_span["status"] == (
105104
SpanStatus.ERROR if task_fails else SpanStatus.OK
@@ -148,15 +147,21 @@ def retry_task(context):
148147
sentry_sdk.get_client().flush()
149148

150149
payloads = [i.payload for i in items]
151-
(execute_span,) = [
152-
# Searching for this span specifically because this is what has the raised exception
153-
p
154-
for p in payloads
155-
if p["attributes"]["sentry.op"] == OP.QUEUE_TASK_HUEY
156-
]
150+
assert len(payloads) == 3
151+
152+
enqueue_span, re_enqueue_span, execute_span = payloads
153+
154+
assert enqueue_span["attributes"]["sentry.op"] == OP.QUEUE_SUBMIT_HUEY
155+
assert enqueue_span["is_segment"]
156+
157+
assert re_enqueue_span["attributes"]["sentry.op"] == OP.QUEUE_SUBMIT_HUEY
158+
assert not re_enqueue_span["is_segment"]
159+
160+
assert execute_span["attributes"]["sentry.op"] == OP.QUEUE_TASK_HUEY
157161
assert execute_span["is_segment"]
158162
assert execute_span["name"] == "retry_task"
159163
assert execute_span["status"] == SpanStatus.OK
164+
160165
assert len(huey) == 1
161166

162167
task = huey.dequeue()
@@ -165,13 +170,10 @@ def retry_task(context):
165170
sentry_sdk.get_client().flush()
166171

167172
all_payloads = [i.payload for i in items]
168-
task_spans = [
169-
p
170-
for p in all_payloads
171-
if p["attributes"]["sentry.op"] == OP.QUEUE_TASK_HUEY
172-
]
173-
assert len(task_spans) == 2
174-
retry_span = task_spans[1]
173+
174+
assert len(all_payloads) == 4
175+
retry_span = all_payloads[3]
176+
175177
assert retry_span["is_segment"]
176178
assert retry_span["name"] == "retry_task"
177179
assert retry_span["status"] == SpanStatus.OK
@@ -194,10 +196,50 @@ def retry_task(context):
194196
assert len(huey) == 0
195197

196198

199+
@pytest.mark.parametrize(
200+
"has_span_streaming", [True, False], ids=["streaming", "no_streaming"]
201+
)
202+
def test_task_cancel_does_not_override_status(
203+
capture_events, capture_items, init_huey, has_span_streaming
204+
):
205+
huey = init_huey(has_span_streaming=has_span_streaming)
206+
207+
@huey.task()
208+
def cancel_task():
209+
raise CancelExecution()
210+
211+
if has_span_streaming:
212+
items = capture_items("span")
213+
execute_huey_task(huey, cancel_task)
214+
sentry_sdk.get_client().flush()
215+
216+
payloads = [i.payload for i in items]
217+
assert len(payloads) == 2
218+
enqueue_span, execute_span = payloads
219+
220+
assert enqueue_span["attributes"]["sentry.op"] == OP.QUEUE_SUBMIT_HUEY
221+
assert execute_span["attributes"]["sentry.op"] == OP.QUEUE_TASK_HUEY
222+
assert execute_span["is_segment"]
223+
assert execute_span["name"] == "cancel_task"
224+
assert execute_span["status"] == SpanStatus.OK
225+
else:
226+
events = capture_events()
227+
execute_huey_task(huey, cancel_task)
228+
229+
(event,) = events
230+
assert event["transaction"] == "cancel_task"
231+
assert event["contexts"]["trace"]["status"] == "aborted"
232+
233+
197234
@pytest.mark.parametrize("lock_name", ["lock.a", "lock.b"], ids=["locked", "unlocked"])
235+
@pytest.mark.parametrize(
236+
"has_span_streaming", [True, False], ids=["streaming", "no_streaming"]
237+
)
198238
@pytest.mark.skipif(HUEY_VERSION < (2, 5), reason="is_locked was added in 2.5")
199-
def test_task_lock(capture_events, init_huey, lock_name):
200-
huey = init_huey()
239+
def test_task_lock(
240+
capture_events, capture_items, init_huey, lock_name, has_span_streaming
241+
):
242+
huey = init_huey(has_span_streaming=has_span_streaming)
201243

202244
task_lock_name = "lock.a"
203245
should_be_locked = task_lock_name == lock_name
@@ -207,19 +249,39 @@ def test_task_lock(capture_events, init_huey, lock_name):
207249
def maybe_locked_task():
208250
pass
209251

210-
events = capture_events()
252+
if has_span_streaming:
253+
items = capture_items("span")
254+
with huey.lock_task(lock_name):
255+
assert huey.is_locked(task_lock_name) == should_be_locked
256+
execute_huey_task(huey, maybe_locked_task)
257+
sentry_sdk.get_client().flush()
211258

212-
with huey.lock_task(lock_name):
213-
assert huey.is_locked(task_lock_name) == should_be_locked
214-
result = execute_huey_task(huey, maybe_locked_task)
259+
payloads = [i.payload for i in items]
260+
assert len(payloads) == 2
261+
enqueue_span, execute_span = payloads
215262

216-
(event,) = events
263+
assert enqueue_span["attributes"]["sentry.op"] == OP.QUEUE_SUBMIT_HUEY
264+
assert execute_span["attributes"]["sentry.op"] == OP.QUEUE_TASK_HUEY
217265

218-
assert event["transaction"] == "maybe_locked_task"
219-
assert event["tags"]["huey_task_id"] == result.task.id
220-
assert (
221-
event["contexts"]["trace"]["status"] == "aborted" if should_be_locked else "ok"
222-
)
266+
assert execute_span["is_segment"]
267+
assert execute_span["name"] == "maybe_locked_task"
268+
assert execute_span["status"] == SpanStatus.OK
269+
else:
270+
events = capture_events()
271+
272+
with huey.lock_task(lock_name):
273+
assert huey.is_locked(task_lock_name) == should_be_locked
274+
result = execute_huey_task(huey, maybe_locked_task)
275+
276+
(event,) = events
277+
278+
assert event["transaction"] == "maybe_locked_task"
279+
assert event["tags"]["huey_task_id"] == result.task.id
280+
assert (
281+
event["contexts"]["trace"]["status"] == "aborted"
282+
if should_be_locked
283+
else "ok"
284+
)
223285
assert len(huey) == 0
224286

225287

0 commit comments

Comments
 (0)