From 7d8716dbce962d844a57896833984bd279f805a2 Mon Sep 17 00:00:00 2001 From: JackiLin Date: Wed, 4 Sep 2024 13:31:36 +0800 Subject: [PATCH 1/4] Fix sentinel mode read-write separation --- django_redis/pool.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/django_redis/pool.py b/django_redis/pool.py index b0e5f2a3..20817992 100644 --- a/django_redis/pool.py +++ b/django_redis/pool.py @@ -1,5 +1,5 @@ from typing import Dict -from urllib.parse import parse_qs, urlparse +from urllib.parse import parse_qs, urlparse,urlencode,urlunparse from django.conf import settings from django.core.exceptions import ImproperlyConfigured @@ -163,15 +163,30 @@ def get_connection_pool(self, params): # explicitly set service_name and sentinel_manager for the # SentinelConnectionPool constructor since will be called by from_url cp_params = dict(params) - cp_params.update(service_name=url.hostname, sentinel_manager=self._sentinel) - pool = super().get_connection_pool(cp_params) - # convert "is_master" to a boolean if set on the URL, otherwise if not # provided it defaults to True. - is_master = parse_qs(url.query).get("is_master") + query_params = parse_qs(url.query) + is_master = query_params.get("is_master") if is_master: - pool.is_master = to_bool(is_master[0]) + cp_params['is_master'] = to_bool(is_master[0]) + # then remove the "is_master" query string from the URL + # so it doesn't interfere with the SentinelConnectionPool constructor + if 'is_master' in query_params: + del query_params['is_master'] + new_query = urlencode(query_params, doseq=True) + + new_url = urlunparse(( + url.scheme, + url.netloc, + url.path, + url.params, + new_query, + url.fragment + )) + + cp_params.update(service_name=url.hostname, sentinel_manager=self._sentinel,url=new_url) + pool = super().get_connection_pool(cp_params) return pool From 82dadbf532d6fab56c39708a2eb27967aae04f84 Mon Sep 17 00:00:00 2001 From: JackiLin Date: Fri, 6 Sep 2024 21:20:09 +0800 Subject: [PATCH 2/4] added changelog,fix ruff --- changelog.d/749.bugfix | 1 + django_redis/client/default.py | 4 ++-- django_redis/client/herd.py | 4 +--- django_redis/pool.py | 26 +++++++++++--------------- 4 files changed, 15 insertions(+), 20 deletions(-) create mode 100644 changelog.d/749.bugfix diff --git a/changelog.d/749.bugfix b/changelog.d/749.bugfix new file mode 100644 index 00000000..731039c1 --- /dev/null +++ b/changelog.d/749.bugfix @@ -0,0 +1 @@ +Fix is_master parsing error for write separation in sentinel mode \ No newline at end of file diff --git a/django_redis/client/default.py b/django_redis/client/default.py index 3219f7c9..420fb618 100644 --- a/django_redis/client/default.py +++ b/django_redis/client/default.py @@ -328,7 +328,7 @@ def expire( # for some strange reason mypy complains, # saying that timeout type is float | timedelta - return client.expire(key, timeout) # type: ignore + return client.expire(key, timeout) def pexpire( self, @@ -349,7 +349,7 @@ def pexpire( # is fixed. # for some strange reason mypy complains, # saying that timeout type is float | timedelta - return bool(client.pexpire(key, timeout)) # type: ignore + return bool(client.pexpire(key, timeout)) def pexpire_at( self, diff --git a/django_redis/client/herd.py b/django_redis/client/herd.py index 94d539cd..c5c6d7d8 100644 --- a/django_redis/client/herd.py +++ b/django_redis/client/herd.py @@ -26,9 +26,7 @@ def _is_expired(x, herd_timeout: int) -> bool: return True val = x + random.randint(1, herd_timeout) - if val >= herd_timeout: - return True - return False + return val >= herd_timeout class HerdClient(DefaultClient): diff --git a/django_redis/pool.py b/django_redis/pool.py index 20817992..780f2e4b 100644 --- a/django_redis/pool.py +++ b/django_redis/pool.py @@ -1,5 +1,5 @@ from typing import Dict -from urllib.parse import parse_qs, urlparse,urlencode,urlunparse +from urllib.parse import parse_qs, urlencode, urlparse, urlunparse from django.conf import settings from django.core.exceptions import ImproperlyConfigured @@ -168,26 +168,22 @@ def get_connection_pool(self, params): query_params = parse_qs(url.query) is_master = query_params.get("is_master") if is_master: - cp_params['is_master'] = to_bool(is_master[0]) + cp_params["is_master"] = to_bool(is_master[0]) # then remove the "is_master" query string from the URL # so it doesn't interfere with the SentinelConnectionPool constructor - if 'is_master' in query_params: - del query_params['is_master'] + if "is_master" in query_params: + del query_params["is_master"] new_query = urlencode(query_params, doseq=True) - new_url = urlunparse(( - url.scheme, - url.netloc, - url.path, - url.params, - new_query, - url.fragment - )) + new_url = urlunparse( + (url.scheme,url.netloc,url.path,url.params,new_query,url.fragment) + ) - cp_params.update(service_name=url.hostname, sentinel_manager=self._sentinel,url=new_url) + cp_params.update( + service_name=url.hostname, sentinel_manager=self._sentinel,url=new_url + ) - pool = super().get_connection_pool(cp_params) - return pool + return super().get_connection_pool(cp_params) def get_connection_factory(path=None, options=None): From 9e56f925b5f415a9d2f7f36fb22f12a1ea6241c3 Mon Sep 17 00:00:00 2001 From: JackiLin Date: Fri, 6 Sep 2024 21:34:51 +0800 Subject: [PATCH 3/4] added testcase --- tests/test_connection_string.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_connection_string.py b/tests/test_connection_string.py index 81dffebe..0ff84b92 100644 --- a/tests/test_connection_string.py +++ b/tests/test_connection_string.py @@ -8,6 +8,8 @@ [ "unix://tmp/foo.bar?db=1", "redis://localhost/2", + "redis://redis-master/0?is_master=0", + "redis://redis-master/2?is_master=False", "rediss://localhost:3333?db=2", ], ) From df4fd4d82728e432d7e49505beba82bf7a412ce3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 13:38:17 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- django_redis/client/default.py | 4 ++-- django_redis/pool.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/django_redis/client/default.py b/django_redis/client/default.py index 420fb618..b0827f72 100644 --- a/django_redis/client/default.py +++ b/django_redis/client/default.py @@ -328,7 +328,7 @@ def expire( # for some strange reason mypy complains, # saying that timeout type is float | timedelta - return client.expire(key, timeout) + return client.expire(key, timeout) def pexpire( self, @@ -349,7 +349,7 @@ def pexpire( # is fixed. # for some strange reason mypy complains, # saying that timeout type is float | timedelta - return bool(client.pexpire(key, timeout)) + return bool(client.pexpire(key, timeout)) def pexpire_at( self, diff --git a/django_redis/pool.py b/django_redis/pool.py index 780f2e4b..7e01f782 100644 --- a/django_redis/pool.py +++ b/django_redis/pool.py @@ -176,11 +176,11 @@ def get_connection_pool(self, params): new_query = urlencode(query_params, doseq=True) new_url = urlunparse( - (url.scheme,url.netloc,url.path,url.params,new_query,url.fragment) + (url.scheme, url.netloc, url.path, url.params, new_query, url.fragment) ) cp_params.update( - service_name=url.hostname, sentinel_manager=self._sentinel,url=new_url + service_name=url.hostname, sentinel_manager=self._sentinel, url=new_url ) return super().get_connection_pool(cp_params)