redis 5.3.0b4__py3-none-any.whl → 6.0.0b1__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 +21 -13
- redis/asyncio/cluster.py +79 -56
- redis/asyncio/connection.py +40 -11
- redis/asyncio/lock.py +26 -5
- redis/asyncio/sentinel.py +9 -1
- redis/asyncio/utils.py +1 -1
- redis/auth/token.py +6 -2
- redis/backoff.py +15 -0
- redis/client.py +80 -59
- redis/cluster.py +114 -52
- redis/commands/cluster.py +1 -11
- redis/commands/core.py +218 -206
- redis/commands/helpers.py +0 -70
- redis/commands/redismodules.py +0 -20
- 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/connection.py +48 -23
- redis/exceptions.py +4 -1
- redis/lock.py +24 -4
- redis/ocsp.py +2 -1
- redis/sentinel.py +1 -1
- redis/typing.py +1 -1
- redis/utils.py +107 -1
- {redis-5.3.0b4.dist-info → redis-6.0.0b1.dist-info}/METADATA +57 -23
- {redis-5.3.0b4.dist-info → redis-6.0.0b1.dist-info}/RECORD +33 -40
- {redis-5.3.0b4.dist-info → redis-6.0.0b1.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.0b4.dist-info/top_level.txt +0 -1
- /redis/commands/search/{indexDefinition.py → index_definition.py} +0 -0
- {redis-5.3.0b4.dist-info → redis-6.0.0b1.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,6 +41,7 @@ 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,
|
|
@@ -54,10 +54,13 @@ def get_node_name(host: str, port: Union[str, int]) -> str:
|
|
|
54
54
|
return f"{host}:{port}"
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
@deprecated_args(
|
|
58
|
+
allowed_args=["redis_node"],
|
|
59
|
+
reason="Use get_connection(redis_node) instead",
|
|
60
|
+
version="5.0.3",
|
|
61
|
+
)
|
|
57
62
|
def get_connection(redis_node, *args, **options):
|
|
58
|
-
return redis_node.connection or redis_node.connection_pool.get_connection(
|
|
59
|
-
args[0], **options
|
|
60
|
-
)
|
|
63
|
+
return redis_node.connection or redis_node.connection_pool.get_connection()
|
|
61
64
|
|
|
62
65
|
|
|
63
66
|
def parse_scan_result(command, res, **options):
|
|
@@ -193,20 +196,6 @@ def cleanup_kwargs(**kwargs):
|
|
|
193
196
|
return connection_kwargs
|
|
194
197
|
|
|
195
198
|
|
|
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
199
|
class AbstractRedisCluster:
|
|
211
200
|
RedisClusterRequestTTL = 16
|
|
212
201
|
|
|
@@ -300,7 +289,6 @@ class AbstractRedisCluster:
|
|
|
300
289
|
"TFUNCTION LIST",
|
|
301
290
|
"TFCALL",
|
|
302
291
|
"TFCALLASYNC",
|
|
303
|
-
"GRAPH.CONFIG",
|
|
304
292
|
"LATENCY HISTORY",
|
|
305
293
|
"LATENCY LATEST",
|
|
306
294
|
"LATENCY RESET",
|
|
@@ -320,7 +308,6 @@ class AbstractRedisCluster:
|
|
|
320
308
|
"FUNCTION LIST",
|
|
321
309
|
"FUNCTION LOAD",
|
|
322
310
|
"FUNCTION RESTORE",
|
|
323
|
-
"REDISGEARS_2.REFRESHCLUSTER",
|
|
324
311
|
"SCAN",
|
|
325
312
|
"SCRIPT EXISTS",
|
|
326
313
|
"SCRIPT FLUSH",
|
|
@@ -496,6 +483,11 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
496
483
|
"""
|
|
497
484
|
return cls(url=url, **kwargs)
|
|
498
485
|
|
|
486
|
+
@deprecated_args(
|
|
487
|
+
args_to_warn=["read_from_replicas"],
|
|
488
|
+
reason="Please configure the 'load_balancing_strategy' instead",
|
|
489
|
+
version="5.0.3",
|
|
490
|
+
)
|
|
499
491
|
def __init__(
|
|
500
492
|
self,
|
|
501
493
|
host: Optional[str] = None,
|
|
@@ -506,6 +498,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
506
498
|
require_full_coverage: bool = False,
|
|
507
499
|
reinitialize_steps: int = 5,
|
|
508
500
|
read_from_replicas: bool = False,
|
|
501
|
+
load_balancing_strategy: Optional["LoadBalancingStrategy"] = None,
|
|
509
502
|
dynamic_startup_nodes: bool = True,
|
|
510
503
|
url: Optional[str] = None,
|
|
511
504
|
address_remap: Optional[Callable[[Tuple[str, int]], Tuple[str, int]]] = None,
|
|
@@ -534,11 +527,16 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
534
527
|
cluster client. If not all slots are covered, RedisClusterException
|
|
535
528
|
will be thrown.
|
|
536
529
|
:param read_from_replicas:
|
|
530
|
+
@deprecated - please use load_balancing_strategy instead
|
|
537
531
|
Enable read from replicas in READONLY mode. You can read possibly
|
|
538
532
|
stale data.
|
|
539
533
|
When set to true, read commands will be assigned between the
|
|
540
534
|
primary and its replications in a Round-Robin manner.
|
|
541
|
-
|
|
535
|
+
:param load_balancing_strategy:
|
|
536
|
+
Enable read from replicas in READONLY mode and defines the load balancing
|
|
537
|
+
strategy that will be used for cluster node selection.
|
|
538
|
+
The data read from replicas is eventually consistent with the data in primary nodes.
|
|
539
|
+
:param dynamic_startup_nodes:
|
|
542
540
|
Set the RedisCluster's startup nodes to all of the discovered nodes.
|
|
543
541
|
If true (default value), the cluster's discovered nodes will be used to
|
|
544
542
|
determine the cluster nodes-slots mapping in the next topology refresh.
|
|
@@ -643,6 +641,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
643
641
|
self.command_flags = self.__class__.COMMAND_FLAGS.copy()
|
|
644
642
|
self.node_flags = self.__class__.NODE_FLAGS.copy()
|
|
645
643
|
self.read_from_replicas = read_from_replicas
|
|
644
|
+
self.load_balancing_strategy = load_balancing_strategy
|
|
646
645
|
self.reinitialize_counter = 0
|
|
647
646
|
self.reinitialize_steps = reinitialize_steps
|
|
648
647
|
if event_dispatcher is None:
|
|
@@ -676,7 +675,10 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
676
675
|
self.close()
|
|
677
676
|
|
|
678
677
|
def __del__(self):
|
|
679
|
-
|
|
678
|
+
try:
|
|
679
|
+
self.close()
|
|
680
|
+
except Exception:
|
|
681
|
+
pass
|
|
680
682
|
|
|
681
683
|
def disconnect_connection_pools(self):
|
|
682
684
|
for node in self.get_nodes():
|
|
@@ -692,10 +694,9 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
692
694
|
Initialize the connection, authenticate and select a database and send
|
|
693
695
|
READONLY if it is set during object initialization.
|
|
694
696
|
"""
|
|
695
|
-
connection.set_parser(ClusterParser)
|
|
696
697
|
connection.on_connect()
|
|
697
698
|
|
|
698
|
-
if self.read_from_replicas:
|
|
699
|
+
if self.read_from_replicas or self.load_balancing_strategy:
|
|
699
700
|
# Sending READONLY command to server to configure connection as
|
|
700
701
|
# readonly. Since each cluster node may change its server type due
|
|
701
702
|
# to a failover, we should establish a READONLY connection
|
|
@@ -822,6 +823,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
822
823
|
cluster_response_callbacks=self.cluster_response_callbacks,
|
|
823
824
|
cluster_error_retry_attempts=self.cluster_error_retry_attempts,
|
|
824
825
|
read_from_replicas=self.read_from_replicas,
|
|
826
|
+
load_balancing_strategy=self.load_balancing_strategy,
|
|
825
827
|
reinitialize_steps=self.reinitialize_steps,
|
|
826
828
|
lock=self._lock,
|
|
827
829
|
)
|
|
@@ -835,6 +837,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
835
837
|
blocking_timeout=None,
|
|
836
838
|
lock_class=None,
|
|
837
839
|
thread_local=True,
|
|
840
|
+
raise_on_release_error: bool = True,
|
|
838
841
|
):
|
|
839
842
|
"""
|
|
840
843
|
Return a new Lock object using key ``name`` that mimics
|
|
@@ -881,6 +884,11 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
881
884
|
thread-1 would see the token value as "xyz" and would be
|
|
882
885
|
able to successfully release the thread-2's lock.
|
|
883
886
|
|
|
887
|
+
``raise_on_release_error`` indicates whether to raise an exception when
|
|
888
|
+
the lock is no longer owned when exiting the context manager. By default,
|
|
889
|
+
this is True, meaning an exception will be raised. If False, the warning
|
|
890
|
+
will be logged and the exception will be suppressed.
|
|
891
|
+
|
|
884
892
|
In some use cases it's necessary to disable thread local storage. For
|
|
885
893
|
example, if you have code where one thread acquires a lock and passes
|
|
886
894
|
that lock instance to a worker thread to release later. If thread
|
|
@@ -898,6 +906,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
898
906
|
blocking=blocking,
|
|
899
907
|
blocking_timeout=blocking_timeout,
|
|
900
908
|
thread_local=thread_local,
|
|
909
|
+
raise_on_release_error=raise_on_release_error,
|
|
901
910
|
)
|
|
902
911
|
|
|
903
912
|
def set_response_callback(self, command, callback):
|
|
@@ -939,7 +948,9 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
939
948
|
# get the node that holds the key's slot
|
|
940
949
|
slot = self.determine_slot(*args)
|
|
941
950
|
node = self.nodes_manager.get_node_from_slot(
|
|
942
|
-
slot,
|
|
951
|
+
slot,
|
|
952
|
+
self.read_from_replicas and command in READ_COMMANDS,
|
|
953
|
+
self.load_balancing_strategy if command in READ_COMMANDS else None,
|
|
943
954
|
)
|
|
944
955
|
return [node]
|
|
945
956
|
|
|
@@ -1163,12 +1174,16 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
1163
1174
|
# refresh the target node
|
|
1164
1175
|
slot = self.determine_slot(*args)
|
|
1165
1176
|
target_node = self.nodes_manager.get_node_from_slot(
|
|
1166
|
-
slot,
|
|
1177
|
+
slot,
|
|
1178
|
+
self.read_from_replicas and command in READ_COMMANDS,
|
|
1179
|
+
self.load_balancing_strategy
|
|
1180
|
+
if command in READ_COMMANDS
|
|
1181
|
+
else None,
|
|
1167
1182
|
)
|
|
1168
1183
|
moved = False
|
|
1169
1184
|
|
|
1170
1185
|
redis_node = self.get_redis_connection(target_node)
|
|
1171
|
-
connection = get_connection(redis_node
|
|
1186
|
+
connection = get_connection(redis_node)
|
|
1172
1187
|
if asking:
|
|
1173
1188
|
connection.send_command("ASKING")
|
|
1174
1189
|
redis_node.parse_response(connection, "ASKING", **kwargs)
|
|
@@ -1244,7 +1259,7 @@ class RedisCluster(AbstractRedisCluster, RedisClusterCommands):
|
|
|
1244
1259
|
|
|
1245
1260
|
raise ClusterError("TTL exhausted.")
|
|
1246
1261
|
|
|
1247
|
-
def close(self):
|
|
1262
|
+
def close(self) -> None:
|
|
1248
1263
|
try:
|
|
1249
1264
|
with self._lock:
|
|
1250
1265
|
if self.nodes_manager:
|
|
@@ -1312,6 +1327,12 @@ class ClusterNode:
|
|
|
1312
1327
|
self.redis_connection.close()
|
|
1313
1328
|
|
|
1314
1329
|
|
|
1330
|
+
class LoadBalancingStrategy(Enum):
|
|
1331
|
+
ROUND_ROBIN = "round_robin"
|
|
1332
|
+
ROUND_ROBIN_REPLICAS = "round_robin_replicas"
|
|
1333
|
+
RANDOM_REPLICA = "random_replica"
|
|
1334
|
+
|
|
1335
|
+
|
|
1315
1336
|
class LoadBalancer:
|
|
1316
1337
|
"""
|
|
1317
1338
|
Round-Robin Load Balancing
|
|
@@ -1321,15 +1342,38 @@ class LoadBalancer:
|
|
|
1321
1342
|
self.primary_to_idx = {}
|
|
1322
1343
|
self.start_index = start_index
|
|
1323
1344
|
|
|
1324
|
-
def get_server_index(
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1345
|
+
def get_server_index(
|
|
1346
|
+
self,
|
|
1347
|
+
primary: str,
|
|
1348
|
+
list_size: int,
|
|
1349
|
+
load_balancing_strategy: LoadBalancingStrategy = LoadBalancingStrategy.ROUND_ROBIN,
|
|
1350
|
+
) -> int:
|
|
1351
|
+
if load_balancing_strategy == LoadBalancingStrategy.RANDOM_REPLICA:
|
|
1352
|
+
return self._get_random_replica_index(list_size)
|
|
1353
|
+
else:
|
|
1354
|
+
return self._get_round_robin_index(
|
|
1355
|
+
primary,
|
|
1356
|
+
list_size,
|
|
1357
|
+
load_balancing_strategy == LoadBalancingStrategy.ROUND_ROBIN_REPLICAS,
|
|
1358
|
+
)
|
|
1329
1359
|
|
|
1330
1360
|
def reset(self) -> None:
|
|
1331
1361
|
self.primary_to_idx.clear()
|
|
1332
1362
|
|
|
1363
|
+
def _get_random_replica_index(self, list_size: int) -> int:
|
|
1364
|
+
return random.randint(1, list_size - 1)
|
|
1365
|
+
|
|
1366
|
+
def _get_round_robin_index(
|
|
1367
|
+
self, primary: str, list_size: int, replicas_only: bool
|
|
1368
|
+
) -> int:
|
|
1369
|
+
server_index = self.primary_to_idx.setdefault(primary, self.start_index)
|
|
1370
|
+
if replicas_only and server_index == 0:
|
|
1371
|
+
# skip the primary node index
|
|
1372
|
+
server_index = 1
|
|
1373
|
+
# Update the index for the next round
|
|
1374
|
+
self.primary_to_idx[primary] = (server_index + 1) % list_size
|
|
1375
|
+
return server_index
|
|
1376
|
+
|
|
1333
1377
|
|
|
1334
1378
|
class NodesManager:
|
|
1335
1379
|
def __init__(
|
|
@@ -1433,7 +1477,21 @@ class NodesManager:
|
|
|
1433
1477
|
# Reset moved_exception
|
|
1434
1478
|
self._moved_exception = None
|
|
1435
1479
|
|
|
1436
|
-
|
|
1480
|
+
@deprecated_args(
|
|
1481
|
+
args_to_warn=["server_type"],
|
|
1482
|
+
reason=(
|
|
1483
|
+
"In case you need select some load balancing strategy "
|
|
1484
|
+
"that will use replicas, please set it through 'load_balancing_strategy'"
|
|
1485
|
+
),
|
|
1486
|
+
version="5.0.3",
|
|
1487
|
+
)
|
|
1488
|
+
def get_node_from_slot(
|
|
1489
|
+
self,
|
|
1490
|
+
slot,
|
|
1491
|
+
read_from_replicas=False,
|
|
1492
|
+
load_balancing_strategy=None,
|
|
1493
|
+
server_type=None,
|
|
1494
|
+
):
|
|
1437
1495
|
"""
|
|
1438
1496
|
Gets a node that servers this hash slot
|
|
1439
1497
|
"""
|
|
@@ -1448,11 +1506,14 @@ class NodesManager:
|
|
|
1448
1506
|
f'"require_full_coverage={self._require_full_coverage}"'
|
|
1449
1507
|
)
|
|
1450
1508
|
|
|
1451
|
-
if read_from_replicas is True:
|
|
1452
|
-
|
|
1509
|
+
if read_from_replicas is True and load_balancing_strategy is None:
|
|
1510
|
+
load_balancing_strategy = LoadBalancingStrategy.ROUND_ROBIN
|
|
1511
|
+
|
|
1512
|
+
if len(self.slots_cache[slot]) > 1 and load_balancing_strategy:
|
|
1513
|
+
# get the server index using the strategy defined in load_balancing_strategy
|
|
1453
1514
|
primary_name = self.slots_cache[slot][0].name
|
|
1454
1515
|
node_idx = self.read_load_balancer.get_server_index(
|
|
1455
|
-
primary_name, len(self.slots_cache[slot])
|
|
1516
|
+
primary_name, len(self.slots_cache[slot]), load_balancing_strategy
|
|
1456
1517
|
)
|
|
1457
1518
|
elif (
|
|
1458
1519
|
server_type is None
|
|
@@ -1641,7 +1702,7 @@ class NodesManager:
|
|
|
1641
1702
|
if len(disagreements) > 5:
|
|
1642
1703
|
raise RedisClusterException(
|
|
1643
1704
|
f"startup_nodes could not agree on a valid "
|
|
1644
|
-
f
|
|
1705
|
+
f"slots cache: {', '.join(disagreements)}"
|
|
1645
1706
|
)
|
|
1646
1707
|
|
|
1647
1708
|
fully_covered = self.check_slots_coverage(tmp_slots)
|
|
@@ -1686,7 +1747,7 @@ class NodesManager:
|
|
|
1686
1747
|
# If initialize was called after a MovedError, clear it
|
|
1687
1748
|
self._moved_exception = None
|
|
1688
1749
|
|
|
1689
|
-
def close(self):
|
|
1750
|
+
def close(self) -> None:
|
|
1690
1751
|
self.default_node = None
|
|
1691
1752
|
for node in self.nodes_cache.values():
|
|
1692
1753
|
if node.redis_connection:
|
|
@@ -1735,7 +1796,7 @@ class ClusterPubSub(PubSub):
|
|
|
1735
1796
|
first command execution. The node will be determined by:
|
|
1736
1797
|
1. Hashing the channel name in the request to find its keyslot
|
|
1737
1798
|
2. Selecting a node that handles the keyslot: If read_from_replicas is
|
|
1738
|
-
set to true, a replica can be selected.
|
|
1799
|
+
set to true or load_balancing_strategy is set, a replica can be selected.
|
|
1739
1800
|
|
|
1740
1801
|
:type redis_cluster: RedisCluster
|
|
1741
1802
|
:type node: ClusterNode
|
|
@@ -1831,7 +1892,9 @@ class ClusterPubSub(PubSub):
|
|
|
1831
1892
|
channel = args[1]
|
|
1832
1893
|
slot = self.cluster.keyslot(channel)
|
|
1833
1894
|
node = self.cluster.nodes_manager.get_node_from_slot(
|
|
1834
|
-
slot,
|
|
1895
|
+
slot,
|
|
1896
|
+
self.cluster.read_from_replicas,
|
|
1897
|
+
self.cluster.load_balancing_strategy,
|
|
1835
1898
|
)
|
|
1836
1899
|
else:
|
|
1837
1900
|
# Get a random node
|
|
@@ -1839,9 +1902,7 @@ class ClusterPubSub(PubSub):
|
|
|
1839
1902
|
self.node = node
|
|
1840
1903
|
redis_connection = self.cluster.get_redis_connection(node)
|
|
1841
1904
|
self.connection_pool = redis_connection.connection_pool
|
|
1842
|
-
self.connection = self.connection_pool.get_connection(
|
|
1843
|
-
"pubsub", self.shard_hint
|
|
1844
|
-
)
|
|
1905
|
+
self.connection = self.connection_pool.get_connection()
|
|
1845
1906
|
# register a callback that re-subscribes to any channels we
|
|
1846
1907
|
# were listening to when we were disconnected
|
|
1847
1908
|
self.connection.register_connect_callback(self.on_connect)
|
|
@@ -1976,6 +2037,7 @@ class ClusterPipeline(RedisCluster):
|
|
|
1976
2037
|
cluster_response_callbacks: Optional[Dict[str, Callable]] = None,
|
|
1977
2038
|
startup_nodes: Optional[List["ClusterNode"]] = None,
|
|
1978
2039
|
read_from_replicas: bool = False,
|
|
2040
|
+
load_balancing_strategy: Optional[LoadBalancingStrategy] = None,
|
|
1979
2041
|
cluster_error_retry_attempts: int = 3,
|
|
1980
2042
|
reinitialize_steps: int = 5,
|
|
1981
2043
|
lock=None,
|
|
@@ -1991,6 +2053,7 @@ class ClusterPipeline(RedisCluster):
|
|
|
1991
2053
|
)
|
|
1992
2054
|
self.startup_nodes = startup_nodes if startup_nodes else []
|
|
1993
2055
|
self.read_from_replicas = read_from_replicas
|
|
2056
|
+
self.load_balancing_strategy = load_balancing_strategy
|
|
1994
2057
|
self.command_flags = self.__class__.COMMAND_FLAGS.copy()
|
|
1995
2058
|
self.cluster_response_callbacks = cluster_response_callbacks
|
|
1996
2059
|
self.cluster_error_retry_attempts = cluster_error_retry_attempts
|
|
@@ -2062,12 +2125,11 @@ class ClusterPipeline(RedisCluster):
|
|
|
2062
2125
|
"""
|
|
2063
2126
|
cmd = " ".join(map(safe_str, command))
|
|
2064
2127
|
msg = (
|
|
2065
|
-
f"Command # {number} ({cmd}) of pipeline "
|
|
2066
|
-
f"caused error: {exception.args[0]}"
|
|
2128
|
+
f"Command # {number} ({cmd}) of pipeline caused error: {exception.args[0]}"
|
|
2067
2129
|
)
|
|
2068
2130
|
exception.args = (msg,) + exception.args[1:]
|
|
2069
2131
|
|
|
2070
|
-
def execute(self, raise_on_error=True):
|
|
2132
|
+
def execute(self, raise_on_error: bool = True) -> List[Any]:
|
|
2071
2133
|
"""
|
|
2072
2134
|
Execute all the commands in the current pipeline
|
|
2073
2135
|
"""
|
|
@@ -2137,7 +2199,7 @@ class ClusterPipeline(RedisCluster):
|
|
|
2137
2199
|
raise_on_error=raise_on_error,
|
|
2138
2200
|
allow_redirections=allow_redirections,
|
|
2139
2201
|
)
|
|
2140
|
-
except
|
|
2202
|
+
except RedisCluster.ERRORS_ALLOW_RETRY as e:
|
|
2141
2203
|
if retry_attempts > 0:
|
|
2142
2204
|
# Try again with the new cluster setup. All other errors
|
|
2143
2205
|
# should be raised.
|
|
@@ -2201,8 +2263,8 @@ class ClusterPipeline(RedisCluster):
|
|
|
2201
2263
|
if node_name not in nodes:
|
|
2202
2264
|
redis_node = self.get_redis_connection(node)
|
|
2203
2265
|
try:
|
|
2204
|
-
connection = get_connection(redis_node
|
|
2205
|
-
except ConnectionError:
|
|
2266
|
+
connection = get_connection(redis_node)
|
|
2267
|
+
except (ConnectionError, TimeoutError):
|
|
2206
2268
|
for n in nodes.values():
|
|
2207
2269
|
n.connection_pool.release(n.connection)
|
|
2208
2270
|
# 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
|
):
|