From e245d5d4a7da4aaf0e76ed833be799d73065fbf2 Mon Sep 17 00:00:00 2001 From: RealFatCat Date: Mon, 25 Sep 2023 15:38:35 +0300 Subject: [PATCH] Connection factory goes to cache options --- README.rst | 29 +++++++++++++ django_redis/pool.py | 3 ++ setup.cfg | 1 + tests/settings/sqlite_sentinel_opts.py | 49 +++++++++++++++++++++ tests/test_connection_factory.py | 60 ++++++++++++++++++++++++++ 5 files changed, 142 insertions(+) create mode 100644 tests/settings/sqlite_sentinel_opts.py create mode 100644 tests/test_connection_factory.py diff --git a/README.rst b/README.rst index ccf323b1..8d597df7 100644 --- a/README.rst +++ b/README.rst @@ -736,6 +736,35 @@ In order to enable this functionality you should add the following: }, } +It is also possible to set some caches as sentinels and some as not: + +.. code-block:: python + + SENTINELS = [ + ('sentinel-1', 26379), + ('sentinel-2', 26379), + ('sentinel-3', 26379), + ] + CACHES = { + "sentinel": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://service_name/db", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.SentinelClient", + "SENTINELS": SENTINELS, + "CONNECTION_POOL_CLASS": "redis.sentinel.SentinelConnectionPool", + "CONNECTION_FACTORY": "django_redis.pool.SentinelConnectionFactory", + }, + }, + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://127.0.0.1:6379/1", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + }, + }, + } + .. _Redis Sentinels: https://redis.io/topics/sentinel Pluggable parsers diff --git a/django_redis/pool.py b/django_redis/pool.py index 44afa943..c5704076 100644 --- a/django_redis/pool.py +++ b/django_redis/pool.py @@ -184,6 +184,9 @@ def get_connection_factory(path=None, options=None): "DJANGO_REDIS_CONNECTION_FACTORY", "django_redis.pool.ConnectionFactory", ) + opt_conn_factory = options.get("CONNECTION_FACTORY") + if opt_conn_factory: + path = opt_conn_factory cls = import_string(path) return cls(options or {}) diff --git a/setup.cfg b/setup.cfg index 9395f819..78032a20 100644 --- a/setup.cfg +++ b/setup.cfg @@ -107,6 +107,7 @@ commands = {envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_lz4 {posargs} {envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_msgpack {posargs} {envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_sentinel {posargs} + {envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_sentinel_opts {posargs} {envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_sharding {posargs} {envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_usock {posargs} {envpython} -m pytest --cov-append --cov-report= --ds=settings.sqlite_zlib {posargs} diff --git a/tests/settings/sqlite_sentinel_opts.py b/tests/settings/sqlite_sentinel_opts.py new file mode 100644 index 00000000..29f079ed --- /dev/null +++ b/tests/settings/sqlite_sentinel_opts.py @@ -0,0 +1,49 @@ +SECRET_KEY = "django_tests_secret_key" + +SENTINELS = [("127.0.0.1", 26379)] + +conn_factory = "django_redis.pool.SentinelConnectionFactory" + +CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": ["redis://default_service?db=5"], + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + "SENTINELS": SENTINELS, + "CONNECTION_FACTORY": conn_factory, + }, + }, + "doesnotexist": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://missing_service?db=1", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + "SENTINELS": SENTINELS, + "CONNECTION_FACTORY": conn_factory, + }, + }, + "sample": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://default_service?db=1", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.SentinelClient", + "SENTINELS": SENTINELS, + "CONNECTION_FACTORY": conn_factory, + }, + }, + "with_prefix": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://default_service?db=1", + "KEY_PREFIX": "test-prefix", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + "SENTINELS": SENTINELS, + "CONNECTION_FACTORY": conn_factory, + }, + }, +} + +INSTALLED_APPS = ["django.contrib.sessions"] + +USE_TZ = False diff --git a/tests/test_connection_factory.py b/tests/test_connection_factory.py new file mode 100644 index 00000000..58427ff6 --- /dev/null +++ b/tests/test_connection_factory.py @@ -0,0 +1,60 @@ +import pytest +from django.core.exceptions import ImproperlyConfigured + +from django_redis import pool + + +def test_connection_factory_redefine_from_opts(): + cf = pool.get_connection_factory( + path="django_redis.pool.ConnectionFactory", + options={ + "CONNECTION_FACTORY": "django_redis.pool.SentinelConnectionFactory", + "SENTINELS": [("127.0.0.1", "26739")], + }, + ) + assert cf.__class__.__name__ == "SentinelConnectionFactory" + + +@pytest.mark.parametrize( + "conn_factory,expected", + [ + ("django_redis.pool.SentinelConnectionFactory", pool.SentinelConnectionFactory), + ("django_redis.pool.ConnectionFactory", pool.ConnectionFactory), + ], +) +def test_connection_factory_opts(conn_factory: str, expected): + cf = pool.get_connection_factory( + path=None, + options={ + "CONNECTION_FACTORY": conn_factory, + "SENTINELS": [("127.0.0.1", "26739")], + }, + ) + assert isinstance(cf, expected) + + +@pytest.mark.parametrize( + "conn_factory,expected", + [ + ("django_redis.pool.SentinelConnectionFactory", pool.SentinelConnectionFactory), + ("django_redis.pool.ConnectionFactory", pool.ConnectionFactory), + ], +) +def test_connection_factory_path(conn_factory: str, expected): + cf = pool.get_connection_factory( + path=conn_factory, + options={ + "SENTINELS": [("127.0.0.1", "26739")], + }, + ) + assert isinstance(cf, expected) + + +def test_connection_factory_no_sentinels(): + with pytest.raises(ImproperlyConfigured): + pool.get_connection_factory( + path=None, + options={ + "CONNECTION_FACTORY": "django_redis.pool.SentinelConnectionFactory", + }, + )