From 4d1698295d5308f72db936bb447b995df80c5266 Mon Sep 17 00:00:00 2001 From: uttam12331 Date: Wed, 5 Aug 2026 14:19:39 +0530 Subject: [PATCH] Pass zrank/zrevrank keys so they are cacheable ZRANK and ZREVRANK are in the client-side cache allow list, but neither set options["keys"], so building the cache key raised ValueError("Cannot create cache key.") whenever caching was enabled. Set options["keys"] = [name] for both, matching the range commands, and add a regression test that caches zrank/zrevrank and checks invalidation. --- redis/commands/core.py | 2 ++ tests/test_cache.py | 54 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/redis/commands/core.py b/redis/commands/core.py index 42e7b9cde9..f434343ab3 100644 --- a/redis/commands/core.py +++ b/redis/commands/core.py @@ -9315,6 +9315,7 @@ def zrank( pieces.append("WITHSCORE") options = {"withscore": withscore, "score_cast_func": score_cast_func} + options["keys"] = [name] return self.execute_command(*pieces, **options) @@ -9439,6 +9440,7 @@ def zrevrank( pieces.append("WITHSCORE") options = {"withscore": withscore, "score_cast_func": score_cast_func} + options["keys"] = [name] return self.execute_command(*pieces, **options) diff --git a/tests/test_cache.py b/tests/test_cache.py index d85cee977a..3564550ffa 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -99,6 +99,60 @@ def test_get_from_given_cache(self, r, r2): "barbar", ] + @pytest.mark.parametrize( + "r", + [ + { + "cache": DefaultCache(CacheConfig(max_size=5)), + "single_connection_client": True, + }, + { + "cache": DefaultCache(CacheConfig(max_size=5)), + "single_connection_client": False, + }, + ], + ids=["single", "pool"], + indirect=True, + ) + @pytest.mark.onlynoncluster + def test_zrank_zrevrank_are_cacheable(self, r, r2): + # ZRANK and ZREVRANK are in the cache allow list but used to pass no + # `keys`, so client-side caching raised + # ValueError("Cannot create cache key."). They must be cacheable under + # the whole key and invalidated when the sorted set changes. + cache = r.get_cache() + r.delete("myzset") + r.zadd("myzset", {"a": 1, "b": 2, "c": 3}) + # populate the local cache (no ValueError) + assert r.zrank("myzset", "b") == 1 + assert r.zrevrank("myzset", "b") == 1 + assert ( + cache.get( + CacheKey( + command="ZRANK", + redis_keys=("myzset",), + redis_args=("ZRANK", "myzset", "b"), + ) + ) + is not None + ) + assert ( + cache.get( + CacheKey( + command="ZREVRANK", + redis_keys=("myzset",), + redis_args=("ZREVRANK", "myzset", "b"), + ) + ) + is not None + ) + # change the sorted set from a second client (causes invalidation) + r2.zadd("myzset", {"aa": 0}) + # Add a small delay to allow invalidation to be processed + time.sleep(0.1) + # rank of "b" shifts from 1 to 2 after inserting a lower-scored member + assert r.zrank("myzset", "b") == 2 + @pytest.mark.parametrize( "r", [