redis 5.3.0b5__py3-none-any.whl → 6.0.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- redis/__init__.py +2 -11
- redis/_parsers/base.py +14 -2
- redis/asyncio/client.py +27 -14
- redis/asyncio/cluster.py +85 -59
- redis/asyncio/connection.py +76 -23
- redis/asyncio/lock.py +26 -5
- redis/asyncio/sentinel.py +11 -1
- redis/asyncio/utils.py +1 -1
- redis/auth/token.py +6 -2
- redis/backoff.py +15 -0
- redis/client.py +23 -14
- redis/cluster.py +112 -48
- redis/commands/cluster.py +1 -11
- redis/commands/core.py +219 -207
- redis/commands/helpers.py +0 -70
- redis/commands/redismodules.py +5 -17
- redis/commands/search/aggregation.py +3 -1
- redis/commands/search/commands.py +41 -14
- redis/commands/search/dialect.py +3 -0
- redis/commands/search/profile_information.py +14 -0
- redis/commands/search/query.py +5 -1
- redis/commands/vectorset/__init__.py +46 -0
- redis/commands/vectorset/commands.py +367 -0
- redis/commands/vectorset/utils.py +94 -0
- redis/connection.py +76 -27
- redis/exceptions.py +4 -1
- redis/lock.py +24 -4
- redis/ocsp.py +2 -1
- redis/sentinel.py +3 -1
- redis/utils.py +114 -1
- {redis-5.3.0b5.dist-info → redis-6.0.0b2.dist-info}/METADATA +57 -23
- {redis-5.3.0b5.dist-info → redis-6.0.0b2.dist-info}/RECORD +35 -39
- {redis-5.3.0b5.dist-info → redis-6.0.0b2.dist-info}/WHEEL +1 -2
- redis/commands/graph/__init__.py +0 -263
- redis/commands/graph/commands.py +0 -313
- redis/commands/graph/edge.py +0 -91
- redis/commands/graph/exceptions.py +0 -3
- redis/commands/graph/execution_plan.py +0 -211
- redis/commands/graph/node.py +0 -88
- redis/commands/graph/path.py +0 -78
- redis/commands/graph/query_result.py +0 -588
- redis-5.3.0b5.dist-info/top_level.txt +0 -1
- /redis/commands/search/{indexDefinition.py → index_definition.py} +0 -0
- {redis-5.3.0b5.dist-info → redis-6.0.0b2.dist-info/licenses}/LICENSE +0 -0
redis/cluster.py
CHANGED
|
@@ -4,6 +4,7 @@ import sys
|
|
|
4
4
|
import threading
|
|
5
5
|
import time
|
|
6
6
|
from collections import OrderedDict
|
|
7
|
+
from enum import Enum
|
|
7
8
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
8
9
|
|
|
9
10
|
from redis._parsers import CommandsParser, Encoder
|
|
@@ -13,7 +14,7 @@ from redis.cache import CacheConfig, CacheFactory, CacheFactoryInterface, CacheI
|
|
|
13
14
|
from redis.client import CaseInsensitiveDict, PubSub, Redis
|
|
14
15
|
from redis.commands import READ_COMMANDS, RedisClusterCommands
|
|
15
16
|
from redis.commands.helpers import list_or_args
|
|
16
|
-
from redis.connection import ConnectionPool,
|
|
17
|
+
from redis.connection import ConnectionPool, parse_url
|
|
17
18
|
from redis.crc import REDIS_CLUSTER_HASH_SLOTS, key_slot
|
|
18
19
|
from redis.event import (
|
|
19
20
|
AfterPooledConnectionsInstantiationEvent,
|
|
@@ -24,12 +25,10 @@ from redis.event import (
|
|
|
24
25
|
from redis.exceptions import (
|
|
25
26
|
AskError,
|
|
26
27
|
AuthenticationError,
|
|
27
|
-
ClusterCrossSlotError,
|
|
28
28
|
ClusterDownError,
|
|
29
29
|
ClusterError,
|
|
30
30
|
ConnectionError,
|
|
31
31
|
DataError,
|
|
32
|
-
MasterDownError,
|
|
33
32
|
MovedError,
|
|
34
33
|
RedisClusterException,
|
|
35
34
|
RedisError,
|
|
@@ -42,11 +41,13 @@ from redis.lock import Lock
|
|
|
42
41
|
from redis.retry import Retry
|
|
43
42
|
from redis.utils import (
|
|
44
43
|
HIREDIS_AVAILABLE,
|
|
44
|
+
deprecated_args,
|
|
45
45
|
dict_merge,
|
|
46
46
|
list_keys_to_dict,
|
|
47
47
|
merge_result,
|
|
48
48
|
safe_str,
|
|
49
49
|
str_if_bytes,
|
|
50
|
+
truncate_text,
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
|
|
@@ -54,10 +55,13 @@ def get_node_name(host: str, port: Union[str, int]) -> str:
|
|
|
54
55
|
return f"{host}:{port}"
|
|
55
56
|
|
|
56
57
|
|
|
58
|
+
@deprecated_args(
|
|
59
|
+
allowed_args=["redis_node"],
|
|
60
|
+
reason="Use get_connection(redis_node) instead",
|
|
61
|
+
version="5.0.3",
|
|
62
|
+
)
|
|
57
63
|
def get_connection(redis_node, *args, **options):
|
|
58
|
-
return redis_node.connection or redis_node.connection_pool.get_connection(
|
|
59
|
-
args[0], **options
|
|
60
|
-
)
|
|
64
|
+
return redis_node.connection or redis_node.connection_pool.get_connection()
|
|
61
65
|
|
|
62
66
|
|
|
63
67
|
def parse_scan_result(command, res, **options):
|
|
@@ -193,20 +197,6 @@ def cleanup_kwargs(**kwargs):
|
|
|
193
197
|
return connection_kwargs
|
|
194
198
|
|
|
195
199
|
|
|
196
|
-
class ClusterParser(DefaultParser):
|
|
197
|
-
EXCEPTION_CLASSES = dict_merge(
|
|
198
|
-
DefaultParser.EXCEPTION_CLASSES,
|
|
199
|
-
{
|
|
200
|
-
"ASK": AskError,
|
|
201
|
-
"TRYAGAIN": TryAgainError,
|
|
202
|
-
"MOVED": MovedError,
|
|
203
|
-
"CLUSTERDOWN": ClusterDownError,
|
|
204
|
-
"CROSSSLOT": ClusterCrossSlotError,
|
|
205
|
-
"MASTERDOWN": MasterDownError,
|
|
206
|
-
},
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
|
|
210
200
|
class AbstractRedisCluster:
|
|
211
201
|
RedisClusterRequestTTL = 16
|
|
212
202
|
|
|
@@ -300,7 +290,6 @@ class AbstractRedisCluster:
|
|
|
300
290
|
"TFUNCTION LIST",
|
|
301
291
|
"TFCALL",
|
|
302
292
|
"TFCALLASYNC",
|
|
303
|
-
"GRAPH.CONFIG",
|
|
304
293
|
"LATENCY HISTORY",
|
|
305
294
|
"LATENCY LATEST",
|
|
306
295
|
"LATENCY RESET",
|
|
@@ -320,7 +309,6 @@ class AbstractRedisCluster:
|
|
|
320
309
|
"FUNCTION LIST",
|
|
321
310
|
"FUNCTION LOAD",
|
|
322
311
|
"FUNCTION RESTORE",
|
|
323
|
-
"REDISGEARS_2.REFRESHCLUSTER",
|
|
324
312
|
"SCAN",
|
|
325
313
|
"SCRIPT EXISTS",
|
|
326
314
|
"SCRIPT FLUSH",
|
|
@@ -496,6 +484,11 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
496
484
|
"""
|
|
497
485
|
return cls(url=url, **kwargs)
|
|
498
486
|
|
|
487
|
+
@deprecated_args(
|
|
488
|
+
args_to_warn=["read_from_replicas"],
|
|
489
|
+
reason="Please configure the 'load_balancing_strategy' instead",
|
|
490
|
+
version="5.0.3",
|
|
491
|
+
)
|
|
499
492
|
def __init__(
|
|
500
493
|
self,
|
|
501
494
|
host: Optional[str] = None,
|
|
@@ -506,6 +499,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
506
499
|
require_full_coverage: bool = False,
|
|
507
500
|
reinitialize_steps: int = 5,
|
|
508
501
|
read_from_replicas: bool = False,
|
|
502
|
+
load_balancing_strategy: Optional["LoadBalancingStrategy"] = None,
|
|
509
503
|
dynamic_startup_nodes: bool = True,
|
|
510
504
|
url: Optional[str] = None,
|
|
511
505
|
address_remap: Optional[Callable[[Tuple[str, int]], Tuple[str, int]]] = None,
|
|
@@ -534,11 +528,16 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
534
528
|
cluster client. If not all slots are covered, RedisClusterException
|
|
535
529
|
will be thrown.
|
|
536
530
|
:param read_from_replicas:
|
|
531
|
+
@deprecated - please use load_balancing_strategy instead
|
|
537
532
|
Enable read from replicas in READONLY mode. You can read possibly
|
|
538
533
|
stale data.
|
|
539
534
|
When set to true, read commands will be assigned between the
|
|
540
535
|
primary and its replications in a Round-Robin manner.
|
|
541
|
-
|
|
536
|
+
:param load_balancing_strategy:
|
|
537
|
+
Enable read from replicas in READONLY mode and defines the load balancing
|
|
538
|
+
strategy that will be used for cluster node selection.
|
|
539
|
+
The data read from replicas is eventually consistent with the data in primary nodes.
|
|
540
|
+
:param dynamic_startup_nodes:
|
|
542
541
|
Set the RedisCluster's startup nodes to all of the discovered nodes.
|
|
543
542
|
If true (default value), the cluster's discovered nodes will be used to
|
|
544
543
|
determine the cluster nodes-slots mapping in the next topology refresh.
|
|
@@ -643,6 +642,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
643
642
|
self.command_flags = self.__class__.COMMAND_FLAGS.copy()
|
|
644
643
|
self.node_flags = self.__class__.NODE_FLAGS.copy()
|
|
645
644
|
self.read_from_replicas = read_from_replicas
|
|
645
|
+
self.load_balancing_strategy = load_balancing_strategy
|
|
646
646
|
self.reinitialize_counter = 0
|
|
647
647
|
self.reinitialize_steps = reinitialize_steps
|
|
648
648
|
if event_dispatcher is None:
|
|
@@ -676,7 +676,10 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
676
676
|
self.close()
|
|
677
677
|
|
|
678
678
|
def __del__(self):
|
|
679
|
-
|
|
679
|
+
try:
|
|
680
|
+
self.close()
|
|
681
|
+
except Exception:
|
|
682
|
+
pass
|
|
680
683
|
|
|
681
684
|
def disconnect_connection_pools(self):
|
|
682
685
|
for node in self.get_nodes():
|
|
@@ -692,10 +695,9 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
692
695
|
Initialize the connection, authenticate and select a database and send
|
|
693
696
|
READONLY if it is set during object initialization.
|
|
694
697
|
"""
|
|
695
|
-
connection.set_parser(ClusterParser)
|
|
696
698
|
connection.on_connect()
|
|
697
699
|
|
|
698
|
-
if self.read_from_replicas:
|
|
700
|
+
if self.read_from_replicas or self.load_balancing_strategy:
|
|
699
701
|
# Sending READONLY command to server to configure connection as
|
|
700
702
|
# readonly. Since each cluster node may change its server type due
|
|
701
703
|
# to a failover, we should establish a READONLY connection
|
|
@@ -822,6 +824,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
822
824
|
cluster_response_callbacks=self.cluster_response_callbacks,
|
|
823
825
|
cluster_error_retry_attempts=self.cluster_error_retry_attempts,
|
|
824
826
|
read_from_replicas=self.read_from_replicas,
|
|
827
|
+
load_balancing_strategy=self.load_balancing_strategy,
|
|
825
828
|
reinitialize_steps=self.reinitialize_steps,
|
|
826
829
|
lock=self._lock,
|
|
827
830
|
)
|
|
@@ -835,6 +838,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
835
838
|
blocking_timeout=None,
|
|
836
839
|
lock_class=None,
|
|
837
840
|
thread_local=True,
|
|
841
|
+
raise_on_release_error: bool = True,
|
|
838
842
|
):
|
|
839
843
|
"""
|
|
840
844
|
Return a new Lock object using key ``name`` that mimics
|
|
@@ -881,6 +885,11 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
881
885
|
thread-1 would see the token value as "xyz" and would be
|
|
882
886
|
able to successfully release the thread-2's lock.
|
|
883
887
|
|
|
888
|
+
``raise_on_release_error`` indicates whether to raise an exception when
|
|
889
|
+
the lock is no longer owned when exiting the context manager. By default,
|
|
890
|
+
this is True, meaning an exception will be raised. If False, the warning
|
|
891
|
+
will be logged and the exception will be suppressed.
|
|
892
|
+
|
|
884
893
|
In some use cases it's necessary to disable thread local storage. For
|
|
885
894
|
example, if you have code where one thread acquires a lock and passes
|
|
886
895
|
that lock instance to a worker thread to release later. If thread
|
|
@@ -898,6 +907,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
898
907
|
blocking=blocking,
|
|
899
908
|
blocking_timeout=blocking_timeout,
|
|
900
909
|
thread_local=thread_local,
|
|
910
|
+
raise_on_release_error=raise_on_release_error,
|
|
901
911
|
)
|
|
902
912
|
|
|
903
913
|
def set_response_callback(self, command, callback):
|
|
@@ -939,7 +949,9 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
939
949
|
# get the node that holds the key's slot
|
|
940
950
|
slot = self.determine_slot(*args)
|
|
941
951
|
node = self.nodes_manager.get_node_from_slot(
|
|
942
|
-
slot,
|
|
952
|
+
slot,
|
|
953
|
+
self.read_from_replicas and command in READ_COMMANDS,
|
|
954
|
+
self.load_balancing_strategy if command in READ_COMMANDS else None,
|
|
943
955
|
)
|
|
944
956
|
return [node]
|
|
945
957
|
|
|
@@ -1163,12 +1175,16 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
1163
1175
|
# refresh the target node
|
|
1164
1176
|
slot = self.determine_slot(*args)
|
|
1165
1177
|
target_node = self.nodes_manager.get_node_from_slot(
|
|
1166
|
-
slot,
|
|
1178
|
+
slot,
|
|
1179
|
+
self.read_from_replicas and command in READ_COMMANDS,
|
|
1180
|
+
self.load_balancing_strategy
|
|
1181
|
+
if command in READ_COMMANDS
|
|
1182
|
+
else None,
|
|
1167
1183
|
)
|
|
1168
1184
|
moved = False
|
|
1169
1185
|
|
|
1170
1186
|
redis_node = self.get_redis_connection(target_node)
|
|
1171
|
-
connection = get_connection(redis_node
|
|
1187
|
+
connection = get_connection(redis_node)
|
|
1172
1188
|
if asking:
|
|
1173
1189
|
connection.send_command("ASKING")
|
|
1174
1190
|
redis_node.parse_response(connection, "ASKING", **kwargs)
|
|
@@ -1312,6 +1328,12 @@ class ClusterNode:
|
|
|
1312
1328
|
self.redis_connection.close()
|
|
1313
1329
|
|
|
1314
1330
|
|
|
1331
|
+
class LoadBalancingStrategy(Enum):
|
|
1332
|
+
ROUND_ROBIN = "round_robin"
|
|
1333
|
+
ROUND_ROBIN_REPLICAS = "round_robin_replicas"
|
|
1334
|
+
RANDOM_REPLICA = "random_replica"
|
|
1335
|
+
|
|
1336
|
+
|
|
1315
1337
|
class LoadBalancer:
|
|
1316
1338
|
"""
|
|
1317
1339
|
Round-Robin Load Balancing
|
|
@@ -1321,15 +1343,38 @@ class LoadBalancer:
|
|
|
1321
1343
|
self.primary_to_idx = {}
|
|
1322
1344
|
self.start_index = start_index
|
|
1323
1345
|
|
|
1324
|
-
def get_server_index(
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1346
|
+
def get_server_index(
|
|
1347
|
+
self,
|
|
1348
|
+
primary: str,
|
|
1349
|
+
list_size: int,
|
|
1350
|
+
load_balancing_strategy: LoadBalancingStrategy = LoadBalancingStrategy.ROUND_ROBIN,
|
|
1351
|
+
) -> int:
|
|
1352
|
+
if load_balancing_strategy == LoadBalancingStrategy.RANDOM_REPLICA:
|
|
1353
|
+
return self._get_random_replica_index(list_size)
|
|
1354
|
+
else:
|
|
1355
|
+
return self._get_round_robin_index(
|
|
1356
|
+
primary,
|
|
1357
|
+
list_size,
|
|
1358
|
+
load_balancing_strategy == LoadBalancingStrategy.ROUND_ROBIN_REPLICAS,
|
|
1359
|
+
)
|
|
1329
1360
|
|
|
1330
1361
|
def reset(self) -> None:
|
|
1331
1362
|
self.primary_to_idx.clear()
|
|
1332
1363
|
|
|
1364
|
+
def _get_random_replica_index(self, list_size: int) -> int:
|
|
1365
|
+
return random.randint(1, list_size - 1)
|
|
1366
|
+
|
|
1367
|
+
def _get_round_robin_index(
|
|
1368
|
+
self, primary: str, list_size: int, replicas_only: bool
|
|
1369
|
+
) -> int:
|
|
1370
|
+
server_index = self.primary_to_idx.setdefault(primary, self.start_index)
|
|
1371
|
+
if replicas_only and server_index == 0:
|
|
1372
|
+
# skip the primary node index
|
|
1373
|
+
server_index = 1
|
|
1374
|
+
# Update the index for the next round
|
|
1375
|
+
self.primary_to_idx[primary] = (server_index + 1) % list_size
|
|
1376
|
+
return server_index
|
|
1377
|
+
|
|
1333
1378
|
|
|
1334
1379
|
class NodesManager:
|
|
1335
1380
|
def __init__(
|
|
@@ -1433,7 +1478,21 @@ class NodesManager:
|
|
|
1433
1478
|
# Reset moved_exception
|
|
1434
1479
|
self._moved_exception = None
|
|
1435
1480
|
|
|
1436
|
-
|
|
1481
|
+
@deprecated_args(
|
|
1482
|
+
args_to_warn=["server_type"],
|
|
1483
|
+
reason=(
|
|
1484
|
+
"In case you need select some load balancing strategy "
|
|
1485
|
+
"that will use replicas, please set it through 'load_balancing_strategy'"
|
|
1486
|
+
),
|
|
1487
|
+
version="5.0.3",
|
|
1488
|
+
)
|
|
1489
|
+
def get_node_from_slot(
|
|
1490
|
+
self,
|
|
1491
|
+
slot,
|
|
1492
|
+
read_from_replicas=False,
|
|
1493
|
+
load_balancing_strategy=None,
|
|
1494
|
+
server_type=None,
|
|
1495
|
+
):
|
|
1437
1496
|
"""
|
|
1438
1497
|
Gets a node that servers this hash slot
|
|
1439
1498
|
"""
|
|
@@ -1448,11 +1507,14 @@ class NodesManager:
|
|
|
1448
1507
|
f'"require_full_coverage={self._require_full_coverage}"'
|
|
1449
1508
|
)
|
|
1450
1509
|
|
|
1451
|
-
if read_from_replicas is True:
|
|
1452
|
-
|
|
1510
|
+
if read_from_replicas is True and load_balancing_strategy is None:
|
|
1511
|
+
load_balancing_strategy = LoadBalancingStrategy.ROUND_ROBIN
|
|
1512
|
+
|
|
1513
|
+
if len(self.slots_cache[slot]) > 1 and load_balancing_strategy:
|
|
1514
|
+
# get the server index using the strategy defined in load_balancing_strategy
|
|
1453
1515
|
primary_name = self.slots_cache[slot][0].name
|
|
1454
1516
|
node_idx = self.read_load_balancer.get_server_index(
|
|
1455
|
-
primary_name, len(self.slots_cache[slot])
|
|
1517
|
+
primary_name, len(self.slots_cache[slot]), load_balancing_strategy
|
|
1456
1518
|
)
|
|
1457
1519
|
elif (
|
|
1458
1520
|
server_type is None
|
|
@@ -1641,7 +1703,7 @@ class NodesManager:
|
|
|
1641
1703
|
if len(disagreements) > 5:
|
|
1642
1704
|
raise RedisClusterException(
|
|
1643
1705
|
f"startup_nodes could not agree on a valid "
|
|
1644
|
-
f
|
|
1706
|
+
f"slots cache: {', '.join(disagreements)}"
|
|
1645
1707
|
)
|
|
1646
1708
|
|
|
1647
1709
|
fully_covered = self.check_slots_coverage(tmp_slots)
|
|
@@ -1735,7 +1797,7 @@ class ClusterPubSub(PubSub):
|
|
|
1735
1797
|
first command execution. The node will be determined by:
|
|
1736
1798
|
1. Hashing the channel name in the request to find its keyslot
|
|
1737
1799
|
2. Selecting a node that handles the keyslot: If read_from_replicas is
|
|
1738
|
-
set to true, a replica can be selected.
|
|
1800
|
+
set to true or load_balancing_strategy is set, a replica can be selected.
|
|
1739
1801
|
|
|
1740
1802
|
:type redis_cluster: RedisCluster
|
|
1741
1803
|
:type node: ClusterNode
|
|
@@ -1831,7 +1893,9 @@ class ClusterPubSub(PubSub):
|
|
|
1831
1893
|
channel = args[1]
|
|
1832
1894
|
slot = self.cluster.keyslot(channel)
|
|
1833
1895
|
node = self.cluster.nodes_manager.get_node_from_slot(
|
|
1834
|
-
slot,
|
|
1896
|
+
slot,
|
|
1897
|
+
self.cluster.read_from_replicas,
|
|
1898
|
+
self.cluster.load_balancing_strategy,
|
|
1835
1899
|
)
|
|
1836
1900
|
else:
|
|
1837
1901
|
# Get a random node
|
|
@@ -1839,9 +1903,7 @@ class ClusterPubSub(PubSub):
|
|
|
1839
1903
|
self.node = node
|
|
1840
1904
|
redis_connection = self.cluster.get_redis_connection(node)
|
|
1841
1905
|
self.connection_pool = redis_connection.connection_pool
|
|
1842
|
-
self.connection = self.connection_pool.get_connection(
|
|
1843
|
-
"pubsub", self.shard_hint
|
|
1844
|
-
)
|
|
1906
|
+
self.connection = self.connection_pool.get_connection()
|
|
1845
1907
|
# register a callback that re-subscribes to any channels we
|
|
1846
1908
|
# were listening to when we were disconnected
|
|
1847
1909
|
self.connection.register_connect_callback(self.on_connect)
|
|
@@ -1976,6 +2038,7 @@ class ClusterPipeline(RedisCluster):
|
|
|
1976
2038
|
cluster_response_callbacks: Optional[Dict[str, Callable]] = None,
|
|
1977
2039
|
startup_nodes: Optional[List["ClusterNode"]] = None,
|
|
1978
2040
|
read_from_replicas: bool = False,
|
|
2041
|
+
load_balancing_strategy: Optional[LoadBalancingStrategy] = None,
|
|
1979
2042
|
cluster_error_retry_attempts: int = 3,
|
|
1980
2043
|
reinitialize_steps: int = 5,
|
|
1981
2044
|
lock=None,
|
|
@@ -1991,6 +2054,7 @@ class ClusterPipeline(RedisCluster):
|
|
|
1991
2054
|
)
|
|
1992
2055
|
self.startup_nodes = startup_nodes if startup_nodes else []
|
|
1993
2056
|
self.read_from_replicas = read_from_replicas
|
|
2057
|
+
self.load_balancing_strategy = load_balancing_strategy
|
|
1994
2058
|
self.command_flags = self.__class__.COMMAND_FLAGS.copy()
|
|
1995
2059
|
self.cluster_response_callbacks = cluster_response_callbacks
|
|
1996
2060
|
self.cluster_error_retry_attempts = cluster_error_retry_attempts
|
|
@@ -2062,7 +2126,7 @@ class ClusterPipeline(RedisCluster):
|
|
|
2062
2126
|
"""
|
|
2063
2127
|
cmd = " ".join(map(safe_str, command))
|
|
2064
2128
|
msg = (
|
|
2065
|
-
f"Command # {number} ({cmd}) of pipeline "
|
|
2129
|
+
f"Command # {number} ({truncate_text(cmd)}) of pipeline "
|
|
2066
2130
|
f"caused error: {exception.args[0]}"
|
|
2067
2131
|
)
|
|
2068
2132
|
exception.args = (msg,) + exception.args[1:]
|
|
@@ -2137,7 +2201,7 @@ class ClusterPipeline(RedisCluster):
|
|
|
2137
2201
|
raise_on_error=raise_on_error,
|
|
2138
2202
|
allow_redirections=allow_redirections,
|
|
2139
2203
|
)
|
|
2140
|
-
except
|
|
2204
|
+
except RedisCluster.ERRORS_ALLOW_RETRY as e:
|
|
2141
2205
|
if retry_attempts > 0:
|
|
2142
2206
|
# Try again with the new cluster setup. All other errors
|
|
2143
2207
|
# should be raised.
|
|
@@ -2201,8 +2265,8 @@ class ClusterPipeline(RedisCluster):
|
|
|
2201
2265
|
if node_name not in nodes:
|
|
2202
2266
|
redis_node = self.get_redis_connection(node)
|
|
2203
2267
|
try:
|
|
2204
|
-
connection = get_connection(redis_node
|
|
2205
|
-
except ConnectionError:
|
|
2268
|
+
connection = get_connection(redis_node)
|
|
2269
|
+
except (ConnectionError, TimeoutError):
|
|
2206
2270
|
for n in nodes.values():
|
|
2207
2271
|
n.connection_pool.release(n.connection)
|
|
2208
2272
|
# Connection retries are being handled in the node's
|
redis/commands/cluster.py
CHANGED
|
@@ -31,13 +31,11 @@ from .core import (
|
|
|
31
31
|
AsyncACLCommands,
|
|
32
32
|
AsyncDataAccessCommands,
|
|
33
33
|
AsyncFunctionCommands,
|
|
34
|
-
AsyncGearsCommands,
|
|
35
34
|
AsyncManagementCommands,
|
|
36
35
|
AsyncModuleCommands,
|
|
37
36
|
AsyncScriptCommands,
|
|
38
37
|
DataAccessCommands,
|
|
39
38
|
FunctionCommands,
|
|
40
|
-
GearsCommands,
|
|
41
39
|
ManagementCommands,
|
|
42
40
|
ModuleCommands,
|
|
43
41
|
PubSubCommands,
|
|
@@ -595,7 +593,7 @@ class ClusterManagementCommands(ManagementCommands):
|
|
|
595
593
|
"CLUSTER SETSLOT", slot_id, state, node_id, target_nodes=target_node
|
|
596
594
|
)
|
|
597
595
|
elif state.upper() == "STABLE":
|
|
598
|
-
raise RedisError('For "stable" state please use '
|
|
596
|
+
raise RedisError('For "stable" state please use cluster_setslot_stable')
|
|
599
597
|
else:
|
|
600
598
|
raise RedisError(f"Invalid slot state: {state}")
|
|
601
599
|
|
|
@@ -693,12 +691,6 @@ class ClusterManagementCommands(ManagementCommands):
|
|
|
693
691
|
self.read_from_replicas = False
|
|
694
692
|
return self.execute_command("READWRITE", target_nodes=target_nodes)
|
|
695
693
|
|
|
696
|
-
def gears_refresh_cluster(self, **kwargs) -> ResponseT:
|
|
697
|
-
"""
|
|
698
|
-
On an OSS cluster, before executing any gears function, you must call this command. # noqa
|
|
699
|
-
"""
|
|
700
|
-
return self.execute_command("REDISGEARS_2.REFRESHCLUSTER", **kwargs)
|
|
701
|
-
|
|
702
694
|
|
|
703
695
|
class AsyncClusterManagementCommands(
|
|
704
696
|
ClusterManagementCommands, AsyncManagementCommands
|
|
@@ -874,7 +866,6 @@ class RedisClusterCommands(
|
|
|
874
866
|
ClusterDataAccessCommands,
|
|
875
867
|
ScriptCommands,
|
|
876
868
|
FunctionCommands,
|
|
877
|
-
GearsCommands,
|
|
878
869
|
ModuleCommands,
|
|
879
870
|
RedisModuleCommands,
|
|
880
871
|
):
|
|
@@ -905,7 +896,6 @@ class AsyncRedisClusterCommands(
|
|
|
905
896
|
AsyncClusterDataAccessCommands,
|
|
906
897
|
AsyncScriptCommands,
|
|
907
898
|
AsyncFunctionCommands,
|
|
908
|
-
AsyncGearsCommands,
|
|
909
899
|
AsyncModuleCommands,
|
|
910
900
|
AsyncRedisModuleCommands,
|
|
911
901
|
):
|