Skip to content

Commit 2388569

Browse files
committed
Changed async clusterPipeline default node to be replaced only if a relevant exception was thrown from that specific node
1 parent 4cfa069 commit 2388569

File tree

2 files changed

+35
-12
lines changed

2 files changed

+35
-12
lines changed

redis/asyncio/cluster.py

+16-12
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,6 @@ async def execute_command(self, *args: EncodableT, **kwargs: Any) -> Any:
643643
command = args[0]
644644
target_nodes = []
645645
target_nodes_specified = False
646-
is_default_node = False
647646
retry_attempts = self.cluster_error_retry_attempts
648647

649648
passed_targets = kwargs.pop("target_nodes", None)
@@ -657,7 +656,10 @@ async def execute_command(self, *args: EncodableT, **kwargs: Any) -> Any:
657656
for _ in range(execute_attempts):
658657
if self._initialize:
659658
await self.initialize()
660-
if is_default_node:
659+
if (
660+
len(target_nodes) == 1
661+
and target_nodes[0] == self.get_default_node()
662+
):
661663
# Replace the default cluster node
662664
self.replace_default_node()
663665
try:
@@ -670,11 +672,6 @@ async def execute_command(self, *args: EncodableT, **kwargs: Any) -> Any:
670672
raise RedisClusterException(
671673
f"No targets were found to execute {args} command on"
672674
)
673-
if (
674-
len(target_nodes) == 1
675-
and target_nodes[0] == self.get_default_node()
676-
):
677-
is_default_node = True
678675

679676
if len(target_nodes) == 1:
680677
# Return the processed result
@@ -1447,7 +1444,6 @@ async def _execute(
14471444
]
14481445

14491446
nodes = {}
1450-
is_default_node = False
14511447
for cmd in todo:
14521448
passed_targets = cmd.kwargs.pop("target_nodes", None)
14531449
if passed_targets and not client._is_node_flag(passed_targets):
@@ -1463,8 +1459,6 @@ async def _execute(
14631459
if len(target_nodes) > 1:
14641460
raise RedisClusterException(f"Too many targets for command {cmd.args}")
14651461
node = target_nodes[0]
1466-
if node == client.get_default_node():
1467-
is_default_node = True
14681462
if node.name not in nodes:
14691463
nodes[node.name] = (node, [])
14701464
nodes[node.name][1].append(cmd)
@@ -1500,8 +1494,18 @@ async def _execute(
15001494
result.args = (msg,) + result.args[1:]
15011495
raise result
15021496

1503-
if is_default_node:
1504-
client.replace_default_node()
1497+
default_node = nodes.get(client.get_default_node().name)
1498+
if default_node is not None:
1499+
# This pipeline execution used the default node, check if we need
1500+
# to replace it.
1501+
# Note: when the error is raised we'll reset the default node in the
1502+
# caller function.
1503+
for cmd in default_node[1]:
1504+
# Check if it has a command that failed with a relevant
1505+
# exception
1506+
if type(cmd.result) in self.__class__.ERRORS_ALLOW_RETRY:
1507+
client.replace_default_node()
1508+
break
15051509

15061510
return [cmd.result for cmd in stack]
15071511

tests/test_asyncio/test_cluster.py

+19
Original file line numberDiff line numberDiff line change
@@ -2612,6 +2612,25 @@ async def test_can_run_concurrent_pipelines(self, r: RedisCluster) -> None:
26122612
*(self.test_multi_key_operation_with_multi_slots(r) for i in range(100)),
26132613
)
26142614

2615+
@pytest.mark.onlycluster
2616+
async def test_cluster_pipeline_with_default_node_error_command(self, r):
2617+
"""
2618+
Test that the default node is being replaced when it raises a relevant exception
2619+
"""
2620+
curr_default_node = r.get_default_node()
2621+
err = ConnectionError("error")
2622+
cmd_count = await r.command_count()
2623+
mock_node_resp_exc(curr_default_node, err)
2624+
async with r.pipeline(transaction=False) as pipe:
2625+
pipe.command_count()
2626+
result = await pipe.execute(raise_on_error=False)
2627+
2628+
assert result[0] == err
2629+
assert r.get_default_node() != curr_default_node
2630+
pipe.command_count()
2631+
result = await pipe.execute(raise_on_error=False)
2632+
assert result[0] == cmd_count
2633+
26152634

26162635
@pytest.mark.ssl
26172636
class TestSSL:

0 commit comments

Comments
 (0)