feat(redis): Support streaming spans #6083
7 issues
High
Spans leak and miss error status when Redis commands raise exceptions - `sentry_sdk/integrations/redis/_sync_common.py:145-158`
The sentry_patched_execute_command function manually calls __enter__() and __exit__(None, None, None) on spans without try/finally protection. When old_execute_command() raises an exception (e.g., connection error, timeout), the spans are never closed because __exit__() is skipped. Additionally, even on normal exit, __exit__ is called with (None, None, None) instead of exception info, preventing the span's error status from being set. This results in resource leaks (unclosed spans) and missing error tracking when Redis operations fail.
Also found at:
sentry_sdk/integrations/redis/_async_common.py:147-148
Medium
Spans not properly closed if Redis command raises exception - `sentry_sdk/integrations/redis/_async_common.py:145-156`
In _sentry_execute_command, when old_execute_command raises an exception, db_span.__exit__() (line 152) and cache_span.__exit__() (line 156) will never be called because they are executed after the await statement without exception handling. This causes span leaks and corrupted span trees where spans remain open indefinitely. The same pattern exists in the sync version.
Also found at:
sentry_sdk/integrations/redis/utils.py:119-125
Span streaming test for pipeline misses redis.commands assertions - `tests/integrations/redis/cluster_asyncio/test_redis_cluster_asyncio.py:160-179`
In test_async_redis_pipeline, when span_streaming=True, the test does not verify the redis.commands data (count and first_ten commands). The expected_first_ten parameter from the parametrize decorator is unused in the streaming branch. This creates a test coverage gap where the pipeline command details are not validated for streaming spans.
Also found at:
tests/integrations/redis/test_redis.py:72-90tests/integrations/redis/cluster/test_redis_cluster.py:152-153
Test checks wrong span index for cache.hit assertion - `tests/integrations/redis/test_redis_cache_module_async.py:293`
Line 293 checks assert "cache.hit" not in spans[1]["data"] but spans[1] is the db.redis span (as stated in line 286), not the cache.put span. The assertion should check spans[2]["data"] since that's the cache.put span being validated in the surrounding assertions (lines 288-304). This test will pass but doesn't actually verify the intended behavior - that cache.put spans don't have a cache.hit attribute.
Also found at:
tests/integrations/redis/test_redis_cache_module.py:330
Pipeline command data is computed but never set for StreamedSpan - `sentry_sdk/integrations/redis/utils.py:127-134`
In _set_pipeline_data, the commands list is built by iterating over commands_seq (lines 119-125), but for StreamedSpan instances, this data is never set on the span. The check at line 127 if not isinstance(span, StreamedSpan) causes the redis.commands data to be skipped entirely. This results in wasted computation and missing telemetry data for streaming spans.
Also found at:
sentry_sdk/integrations/redis/utils.py:115-117
Test doesn't verify PII filtering for streaming spans - `tests/integrations/redis/test_redis.py:72-90`
The test_redis_pipeline test parametrizes send_default_pii and expected_first_ten but the streaming branch (lines 72-90) completely ignores these parameters. When span_streaming=True, the test only verifies the span name and op, but doesn't check whether sensitive data (like the values in SET commands) is properly filtered based on the send_default_pii setting. This means the PII filtering behavior could be broken in streaming mode without any test failure.
Test assertion checks wrong span index, always passes even if cache.put spans have cache.hit - `tests/integrations/redis/test_redis_cache_module.py:330`
Line 330 asserts "cache.hit" not in spans[1]["data"] but spans[1] is a db.redis span, not the cache.put span being tested. The surrounding assertions (lines 325-341) all check spans[2], which is the cache.put span. This test will always pass because db.redis spans never have cache.hit, making it unable to catch regressions where cache.put spans incorrectly include cache.hit.
Also found at:
tests/integrations/redis/test_redis_cache_module_async.py:293
4 skills analyzed
| Skill | Findings | Duration | Cost |
|---|---|---|---|
| code-review | 3 | 3m 50s | $4.20 |
| find-bugs | 4 | 7m 40s | $6.80 |
| skill-scanner | 0 | 8m 1s | $1.53 |
| security-review | 0 | 9m 2s | $1.63 |
Duration: 28m 33s · Tokens: 8.4M in / 97.2k out · Cost: $14.18 (+merge: $0.01, +fix_gate: $0.01, +dedup: $0.01, +extraction: $0.01)