Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/test-integrations-dbs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ jobs:
run: |
set -x # print commands that are executed
./scripts/runtox.sh "py${{ matrix.python-version }}-redis"
- name: Test redis_py_cluster_legacy
run: |
set -x # print commands that are executed
./scripts/runtox.sh "py${{ matrix.python-version }}-redis_py_cluster_legacy"
- name: Test sqlalchemy
run: |
set -x # print commands that are executed
Expand Down
3 changes: 1 addition & 2 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,7 @@ Additionally, the OpenAI integration now sets `gen_ai.operation.name` to `chat`
- Dropped support for Huey below 2.0.
- Dropped support for Litestar below 2.0.
- Dropped support for PySpark below 3.0.
- Dropped support for redis-py below 4.2.
- Dropped support for the deprecated `redis-py-cluster` package (`rediscluster`). Use redis-py's built-in cluster support (`redis.cluster.RedisCluster`) instead, which the `RedisIntegration` instruments.
- Dropped support for redis-py below 3.4.1.
- Dropped support for Pydantic AI below 1.76.
- Dropped support for OpenAI Agents below 0.10.3.
- Dropped support for MCP below 2.0.
Expand Down
5 changes: 5 additions & 0 deletions scripts/populate_tox/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,11 @@
"py3.7,py3.8": ["fakeredis<2.26.0"],
},
},
"redis_py_cluster_legacy": {
"package": "redis-py-cluster",
"include": ">=2.1",
"num_versions": 2,
},
"requests": {
"package": "requests",
"num_versions": 2,
Expand Down
7 changes: 5 additions & 2 deletions scripts/populate_tox/package_dependencies.jsonl

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions scripts/split_tox_gh_actions/split_tox_gh_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
"clickhouse_driver",
"pymongo",
"redis",
"redis_py_cluster_legacy",
"sqlalchemy",
],
"Flags": [
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def iter_default_integrations(
"pyreqwest": (0, 11, 6),
"quart": (0, 19, 0),
"ray": (2, 7, 0),
"redis": (4, 2, 0),
"redis": (3, 4, 1),
"requests": (2, 30, 0),
"rq": (1, 0),
"sanic": (22, 0),
Expand Down
2 changes: 2 additions & 0 deletions sentry_sdk/integrations/redis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from sentry_sdk.integrations.redis.rb import _patch_rb
from sentry_sdk.integrations.redis.redis import _patch_redis
from sentry_sdk.integrations.redis.redis_cluster import _patch_redis_cluster
from sentry_sdk.integrations.redis.redis_py_cluster_legacy import _patch_rediscluster
from sentry_sdk.utils import parse_version

if TYPE_CHECKING:
Expand Down Expand Up @@ -32,3 +33,4 @@ def setup_once() -> None:
_patch_redis()
_patch_redis_cluster()
_patch_rb()
_patch_rediscluster()
29 changes: 17 additions & 12 deletions sentry_sdk/integrations/redis/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ def _get_redis_command_args(command: "Any") -> "Sequence[Any]":


def _patch_redis() -> None:
import redis.asyncio
from redis import Redis, client

patch_redis_client(
Expand All @@ -40,14 +39,20 @@ def _patch_redis() -> None:
set_db_data_fn=_set_db_data,
)

patch_redis_async_client(
redis.asyncio.client.Redis,
is_cluster=False,
set_db_data_fn=_set_db_data,
)
patch_redis_async_pipeline(
redis.asyncio.client.Pipeline,
False,
_get_redis_command_args,
set_db_data_fn=_set_db_data,
)
# redis.asyncio was added in redis-py 4.2
try:
import redis.asyncio
except ImportError:
pass
else:
patch_redis_async_client(
redis.asyncio.client.Redis,
is_cluster=False,
set_db_data_fn=_set_db_data,
)
patch_redis_async_pipeline(
redis.asyncio.client.Pipeline,
False,
_get_redis_command_args,
set_db_data_fn=_set_db_data,
)
32 changes: 32 additions & 0 deletions sentry_sdk/integrations/redis/redis_py_cluster_legacy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
Instrumentation for redis-py-cluster
The project redis-py-cluster is EOL and was integrated into redis-py starting from version 4.1.0 (Dec 26, 2021).

https://github.com/grokzen/redis-py-cluster
"""

from sentry_sdk.integrations.redis._sync_common import (
patch_redis_client,
patch_redis_pipeline,
)
from sentry_sdk.integrations.redis.modules.queries import _set_db_data
from sentry_sdk.integrations.redis.utils import _parse_rediscluster_command


def _patch_rediscluster() -> None:
try:
import rediscluster # type: ignore
except ImportError:
return

patch_redis_client(
rediscluster.RedisCluster,
is_cluster=True,
set_db_data_fn=_set_db_data,
)
patch_redis_pipeline(
rediscluster.pipeline.ClusterPipeline,
is_cluster=True,
get_command_args_fn=_parse_rediscluster_command,
set_db_data_fn=_set_db_data,
)
3 changes: 3 additions & 0 deletions tests/integrations/redis_py_cluster_legacy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import pytest

pytest.importorskip("rediscluster")
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from unittest import mock

import pytest
import rediscluster

import sentry_sdk
from sentry_sdk import capture_message
from sentry_sdk.consts import OP, SPANDATA
from sentry_sdk.integrations.redis import RedisIntegration

MOCK_CONNECTION_POOL = mock.MagicMock()
MOCK_CONNECTION_POOL.connection_kwargs = {
"host": "localhost",
"port": 63791,
"db": 1,
}


@pytest.fixture(autouse=True)
def monkeypatch_rediscluster_classes(reset_integrations):
pipeline_cls = rediscluster.pipeline.ClusterPipeline
rediscluster.RedisCluster.pipeline = lambda *_, **__: pipeline_cls(
connection_pool=MOCK_CONNECTION_POOL
)
pipeline_cls.execute = lambda *_, **__: None
rediscluster.RedisCluster.execute_command = lambda *_, **__: None


def test_rediscluster_breadcrumb(sentry_init, capture_events):
sentry_init(integrations=[RedisIntegration()])
events = capture_events()

rc = rediscluster.RedisCluster(connection_pool=MOCK_CONNECTION_POOL)
rc.get("foobar")
capture_message("hi")

(event,) = events
(crumb,) = event["breadcrumbs"]["values"]

assert crumb == {
"category": "redis",
"message": "GET 'foobar'",
"data": {
"db.operation": "GET",
"redis.key": "foobar",
"redis.command": "GET",
"redis.is_cluster": True,
},
"timestamp": crumb["timestamp"],
"type": "redis",
}


@pytest.mark.parametrize(
"data_collection, description",
[
({"database_query_data": False}, "SET 'bar' [Filtered]"),
({"database_query_data": True}, "SET 'bar' 1"),
],
)
def test_rediscluster_basic(sentry_init, capture_items, data_collection, description):
sentry_init(
integrations=[RedisIntegration()],
traces_sample_rate=1.0,
data_collection=data_collection,
)
items = capture_items("span")

rc = rediscluster.RedisCluster(connection_pool=MOCK_CONNECTION_POOL)
with sentry_sdk.start_span(name="custom parent"):
rc.set("bar", 1)
sentry_sdk.flush()

span, parent_span = [item.payload for item in items]
assert parent_span["name"] == "custom parent"
assert span["name"] == description

attrs = span["attributes"]
assert attrs["sentry.op"] == OP.DB_QUERY
assert attrs["sentry.origin"] == "auto.db.redis"
assert attrs[SPANDATA.DB_SYSTEM_NAME] == "redis"
assert attrs[SPANDATA.DB_NAMESPACE] == "1"
assert attrs[SPANDATA.SERVER_ADDRESS] == "localhost"
assert attrs[SPANDATA.SERVER_PORT] == 63791
assert attrs[SPANDATA.DB_OPERATION_NAME] == "SET"
assert attrs["db.redis.key"] == "bar"


def test_rediscluster_pipeline(sentry_init, capture_items):
sentry_init(
integrations=[RedisIntegration()],
traces_sample_rate=1.0,
)
items = capture_items("span")

rc = rediscluster.RedisCluster(connection_pool=MOCK_CONNECTION_POOL)
with sentry_sdk.start_span(name="custom parent"):
pipeline = rc.pipeline()
pipeline.get("foo")
pipeline.set("bar", 1)
pipeline.set("baz", 2)
pipeline.execute()
sentry_sdk.flush()

span, parent_span = [item.payload for item in items]
assert parent_span["name"] == "custom parent"
assert span["name"] == "redis.pipeline.execute"

attrs = span["attributes"]
assert attrs["sentry.op"] == OP.DB_QUERY
assert attrs["sentry.origin"] == "auto.db.redis"
assert attrs[SPANDATA.DB_SYSTEM_NAME] == "redis"
assert attrs[SPANDATA.DB_NAMESPACE] == "1"
assert attrs[SPANDATA.SERVER_ADDRESS] == "localhost"
assert attrs[SPANDATA.SERVER_PORT] == 63791
71 changes: 40 additions & 31 deletions tox.ini

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading