Skip to content

Commit da98041

Browse files
authored
fix(redis): Make quiet_redis_noise group RedisClusterExceptions that escape (#101547)
The aim of quiet_redis_noise is to apply standard refinements to Redis error reporting to code within the context. RedisClusterExceptions are grouped because they can be raised nearly anywhere, but that grouping is lost when (as is common) they escape the context. This ensures they are reported within the context, avoiding violating expectations.
1 parent a4831ee commit da98041

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

src/sentry/utils/exceptions.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,12 @@ def quiet_redis_noise() -> Generator[None]:
115115
exception_grouping_context({RedisClusterException: "redis.redis_cluster_exception"}),
116116
set_sentry_exception_levels({TimeoutError: "info", MovedError: "info"}),
117117
):
118-
yield
118+
try:
119+
yield
120+
except RedisClusterException:
121+
# RedisClusterException, unlike the others, propagates in most cases, so we
122+
# want to report it within the grouping context to ensure it is grouped correctly.
123+
# Unless there's another exception in this execution context in the interim, any
124+
# top-level reporting should be dropped by the Sentry dupe detection.
125+
sentry_sdk.capture_exception()
126+
raise

tests/sentry/utils/test_exceptions.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
from typing import Any
33
from unittest.mock import Mock, patch
44

5+
from rediscluster.exceptions import RedisClusterException # type: ignore[attr-defined]
6+
57
from sentry.taskworker.state import CurrentTaskState
68
from sentry.taskworker.workerchild import ProcessingDeadlineExceeded
79
from sentry.utils.exceptions import (
810
exception_grouping_context,
11+
quiet_redis_noise,
912
set_sentry_exception_levels,
1013
timeout_grouping_context,
1114
)
@@ -447,3 +450,26 @@ def test_basic_functionality_minimal_mocking(self) -> None:
447450
# Verify the level was changed and other data preserved
448451
assert result["level"] == "warning"
449452
assert result["other_data"] == "preserved"
453+
454+
455+
class TestQuietRedisNoise:
456+
def test_redis_cluster_exception_captured_and_reraised(self) -> None:
457+
458+
with patch("sentry_sdk.capture_exception") as mock_capture:
459+
try:
460+
with quiet_redis_noise():
461+
raise RedisClusterException("Cannot connect to Redis")
462+
except RedisClusterException:
463+
mock_capture.assert_called_once()
464+
else:
465+
assert False, "RedisClusterException was not re-raised"
466+
467+
def test_other_exceptions_not_captured(self) -> None:
468+
with patch("sentry_sdk.capture_exception") as mock_capture:
469+
try:
470+
with quiet_redis_noise():
471+
raise ValueError("Some other error")
472+
except ValueError:
473+
mock_capture.assert_not_called()
474+
else:
475+
assert False, "ValueError was not re-raised"

0 commit comments

Comments
 (0)