Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
8 changes: 7 additions & 1 deletion docs/lua_scripting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,10 @@ The following commands are not supported:
- ``EVAL_RO``
- ``EVALSHA_RO``

Using scripting within pipelines in cluster mode is **not supported**.
``EVALSHA`` can be used inside a ``ClusterPipeline``. Keys must still map to
the same hash slot (or ``numkeys`` may be ``0``, in which case the command is
routed to a random primary). The script must already be loaded on the target
node, for example via ``SCRIPT LOAD`` on the cluster client, which loads the
script on all primaries. Other scripting helpers such as ``EVAL``,
``load_scripts``, and ``script_load_for_pipeline`` remain unsupported on
cluster pipelines.
1 change: 0 additions & 1 deletion redis/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -3641,7 +3641,6 @@ def inner(*args, **kwargs):
"CONFIG",
"DBSIZE",
"ECHO",
"EVALSHA",
"FLUSHALL",
"FLUSHDB",
"INFO",
Expand Down
43 changes: 43 additions & 0 deletions tests/test_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from redis.cluster import (
PRIMARY,
PIPELINE_BLOCKED_COMMANDS,
REDIS_CLUSTER_HASH_SLOTS,
REPLICA,
ClusterNode,
Expand Down Expand Up @@ -3835,6 +3836,35 @@ def test_disconnect_calls_disconnect_on_existing_connections(self, r):
mock_node_conn.disconnect.assert_called_once()


def test_evalsha_not_in_pipeline_blocked_commands():
"""EVALSHA must be usable in ClusterPipeline (see #2914)."""
assert "EVALSHA" not in PIPELINE_BLOCKED_COMMANDS
Comment thread
ahmed5145 marked this conversation as resolved.


def test_evalsha_can_be_queued_on_cluster_pipeline():
"""
Queuing EVALSHA on a ClusterPipeline must not raise the historical
'blocked when running redis in cluster mode' error. Slot selection for
EVALSHA is already handled by RedisCluster.determine_slot().
"""
r = get_mocked_redis_client(host=default_host, port=default_port)
try:
pipe = r.pipeline()
sha = "a" * 40
returned = pipe.evalsha(sha, 1, "foo", "bar")
assert returned is pipe

queue = pipe._execution_strategy.command_queue
assert len(queue) == 1
assert queue[0].args == ("EVALSHA", sha, 1, "foo", "bar")

assert r.determine_slot("EVALSHA", sha, 1, "foo", "bar") == key_slot(b"foo")
zero_key_slot = r.determine_slot("EVALSHA", sha, 0)
Comment thread
ahmed5145 marked this conversation as resolved.
Outdated
assert 0 <= zero_key_slot < REDIS_CLUSTER_HASH_SLOTS
finally:
r.close()


@pytest.mark.onlycluster
class TestClusterPipeline:
"""
Expand Down Expand Up @@ -3870,6 +3900,19 @@ def test_blocked_arguments(self, r):
str(ex.value).startswith("shard_hint is deprecated in cluster mode") is True
)

def test_evalsha_in_pipeline(self, r):
"""
EVALSHA is allowed in ClusterPipeline when keys map to one slot
and the script is already loaded on cluster primaries (#2914).
"""
multiply = "return redis.call('GET', KEYS[1]) * ARGV[1]"
sha = r.script_load(multiply)
# hash tag keeps the key on a known slot for the pipeline
r.set("{user}a", 2)
with r.pipeline() as pipe:
pipe.evalsha(sha, 1, "{user}a", 3)
assert pipe.execute() == [6]

def test_redis_cluster_pipeline(self, r):
"""
Test that we can use a pipeline with the RedisCluster class
Expand Down