feat(redis): Support streaming spans #6083
5 issues
High
Span leaks when Redis command throws exception - `sentry_sdk/integrations/redis/_async_common.py:145-147`
The _sentry_execute_command function manually enters db_span and cache_span contexts (lines 126 and 145) but doesn't use try/finally to ensure __exit__ is called on exceptions. If await old_execute_command() raises an exception, the spans' __exit__ methods are never called, causing span leaks and potentially corrupting scope state. The synchronous version in _sync_common.py correctly uses try/finally for this pattern.
Medium
NameError raised when Redis command fails with exception - `sentry_sdk/integrations/redis/_sync_common.py:158`
In sentry_patched_execute_command, when old_execute_command raises an exception, the value variable is never assigned. The finally block then attempts to use value in _set_cache_data(cache_span, self, cache_properties, value), causing a NameError. While capture_internal_exceptions() catches this, it results in unnecessary internal exception noise and obscures the real error from monitoring.
Missing exception handling in async _sentry_execute_command causes span leak on Redis errors - `sentry_sdk/integrations/redis/_async_common.py:145-147`
The async _sentry_execute_command function does not wrap the Redis command execution in a try/finally block. When old_execute_command raises an exception, db_span.__exit__() and cache_span.__exit__() are never called, leaving spans unclosed. The sync version in _sync_common.py correctly uses try/finally (lines 151-160).
Also found at:
sentry_sdk/integrations/redis/_async_common.py:147-148
UnboundLocalError when Redis command raises exception - `sentry_sdk/integrations/redis/_sync_common.py:158`
In sentry_patched_execute_command, the finally block calls _set_cache_data(cache_span, self, cache_properties, value) which references value. However, value is only assigned if old_execute_command succeeds. If old_execute_command raises an exception, value is undefined, causing an UnboundLocalError in the finally block. While capture_internal_exceptions() wraps this call and will suppress the error, this still creates an unnecessary exception that gets silently caught, and the span cleanup logic after it (cache_span.__exit__) will still execute successfully.
Low
Missing test coverage for StreamedSpan code path in Redis cache module - `sentry_sdk/integrations/redis/modules/caches.py:89-92`
The new StreamedSpan code path in _set_cache_data lacks dedicated test coverage. Existing Redis tests do not include tests with _experiments={"trace_lifecycle": "stream"} enabled. While the implementation correctly uses isinstance checks to differentiate span types and both set_attribute and set_data have compatible signatures, the new streaming path is untested.
4 skills analyzed
| Skill | Findings | Duration | Cost |
|---|---|---|---|
| code-review | 3 | 3m 54s | $1.84 |
| find-bugs | 2 | 2m 30s | $2.19 |
| skill-scanner | 0 | 40.1s | $0.73 |
| security-review | 0 | 1m 8s | $0.81 |
Duration: 8m 12s · Tokens: 3.2M in / 37.1k out · Cost: $5.60 (+extraction: $0.01, +merge: $0.00, +fix_gate: $0.01, +dedup: $0.00)