@@ -584,7 +584,8 @@ def __init__(
584
584
self .retry = retry
585
585
kwargs .update ({"retry" : self .retry })
586
586
else :
587
- kwargs .update ({"retry" : Retry (default_backoff (), 0 )})
587
+ self .retry = Retry (default_backoff (), 0 )
588
+ kwargs ["retry" ] = self .retry
588
589
589
590
self .encoder = Encoder (
590
591
kwargs .get ("encoding" , "utf-8" ),
@@ -767,6 +768,7 @@ def pipeline(self, transaction=None, shard_hint=None):
767
768
read_from_replicas = self .read_from_replicas ,
768
769
reinitialize_steps = self .reinitialize_steps ,
769
770
lock = self ._lock ,
771
+ retry = self .retry ,
770
772
)
771
773
772
774
def lock (
@@ -850,41 +852,49 @@ def set_response_callback(self, command, callback):
850
852
def _determine_nodes (self , * args , ** kwargs ) -> List ["ClusterNode" ]:
851
853
# Determine which nodes should be executed the command on.
852
854
# Returns a list of target nodes.
853
- command = args [0 ].upper ()
854
- if len (args ) >= 2 and f"{ args [0 ]} { args [1 ]} " .upper () in self .command_flags :
855
- command = f"{ args [0 ]} { args [1 ]} " .upper ()
856
-
857
- nodes_flag = kwargs .pop ("nodes_flag" , None )
858
- if nodes_flag is not None :
859
- # nodes flag passed by the user
860
- command_flag = nodes_flag
861
- else :
862
- # get the nodes group for this command if it was predefined
863
- command_flag = self .command_flags .get (command )
864
- if command_flag == self .__class__ .RANDOM :
865
- # return a random node
866
- return [self .get_random_node ()]
867
- elif command_flag == self .__class__ .PRIMARIES :
868
- # return all primaries
869
- return self .get_primaries ()
870
- elif command_flag == self .__class__ .REPLICAS :
871
- # return all replicas
872
- return self .get_replicas ()
873
- elif command_flag == self .__class__ .ALL_NODES :
874
- # return all nodes
875
- return self .get_nodes ()
876
- elif command_flag == self .__class__ .DEFAULT_NODE :
877
- # return the cluster's default node
878
- return [self .nodes_manager .default_node ]
879
- elif command in self .__class__ .SEARCH_COMMANDS [0 ]:
880
- return [self .nodes_manager .default_node ]
881
- else :
882
- # get the node that holds the key's slot
883
- slot = self .determine_slot (* args )
884
- node = self .nodes_manager .get_node_from_slot (
885
- slot , self .read_from_replicas and command in READ_COMMANDS
886
- )
887
- return [node ]
855
+ try :
856
+ command = args [0 ].upper ()
857
+ if len (args ) >= 2 and f"{ args [0 ]} { args [1 ]} " .upper () in self .command_flags :
858
+ command = f"{ args [0 ]} { args [1 ]} " .upper ()
859
+
860
+ nodes_flag = kwargs .pop ("nodes_flag" , None )
861
+ if nodes_flag is not None :
862
+ # nodes flag passed by the user
863
+ command_flag = nodes_flag
864
+ else :
865
+ # get the nodes group for this command if it was predefined
866
+ command_flag = self .command_flags .get (command )
867
+ if command_flag == self .__class__ .RANDOM :
868
+ # return a random node
869
+ return [self .get_random_node ()]
870
+ elif command_flag == self .__class__ .PRIMARIES :
871
+ # return all primaries
872
+ return self .get_primaries ()
873
+ elif command_flag == self .__class__ .REPLICAS :
874
+ # return all replicas
875
+ return self .get_replicas ()
876
+ elif command_flag == self .__class__ .ALL_NODES :
877
+ # return all nodes
878
+ return self .get_nodes ()
879
+ elif command_flag == self .__class__ .DEFAULT_NODE :
880
+ # return the cluster's default node
881
+ return [self .nodes_manager .default_node ]
882
+ elif command in self .__class__ .SEARCH_COMMANDS [0 ]:
883
+ return [self .nodes_manager .default_node ]
884
+ else :
885
+ # get the node that holds the key's slot
886
+ slot = self .determine_slot (* args )
887
+ node = self .nodes_manager .get_node_from_slot (
888
+ slot , self .read_from_replicas and command in READ_COMMANDS
889
+ )
890
+ return [node ]
891
+ except SlotNotCoveredError as e :
892
+ self .reinitialize_counter += 1
893
+ if self ._should_reinitialized ():
894
+ self .nodes_manager .initialize ()
895
+ # Reset the counter
896
+ self .reinitialize_counter = 0
897
+ raise e
888
898
889
899
def _should_reinitialized (self ):
890
900
# To reinitialize the cluster on every MOVED error,
@@ -1076,6 +1086,12 @@ def execute_command(self, *args, **kwargs):
1076
1086
# The nodes and slots cache were reinitialized.
1077
1087
# Try again with the new cluster setup.
1078
1088
retry_attempts -= 1
1089
+ if self .retry and isinstance (e , self .retry ._supported_errors ):
1090
+ backoff = self .retry ._backoff .compute (
1091
+ self .cluster_error_retry_attempts - retry_attempts
1092
+ )
1093
+ if backoff > 0 :
1094
+ time .sleep (backoff )
1079
1095
continue
1080
1096
else :
1081
1097
# raise the exception
@@ -1135,8 +1151,6 @@ def _execute_command(self, target_node, *args, **kwargs):
1135
1151
# Remove the failed node from the startup nodes before we try
1136
1152
# to reinitialize the cluster
1137
1153
self .nodes_manager .startup_nodes .pop (target_node .name , None )
1138
- # Reset the cluster node's connection
1139
- target_node .redis_connection = None
1140
1154
self .nodes_manager .initialize ()
1141
1155
raise e
1142
1156
except MovedError as e :
@@ -1156,6 +1170,13 @@ def _execute_command(self, target_node, *args, **kwargs):
1156
1170
else :
1157
1171
self .nodes_manager .update_moved_exception (e )
1158
1172
moved = True
1173
+ except SlotNotCoveredError as e :
1174
+ self .reinitialize_counter += 1
1175
+ if self ._should_reinitialized ():
1176
+ self .nodes_manager .initialize ()
1177
+ # Reset the counter
1178
+ self .reinitialize_counter = 0
1179
+ raise e
1159
1180
except TryAgainError :
1160
1181
if ttl < self .RedisClusterRequestTTL / 2 :
1161
1182
time .sleep (0.05 )
@@ -1387,7 +1408,10 @@ def get_node_from_slot(self, slot, read_from_replicas=False, server_type=None):
1387
1408
# randomly choose one of the replicas
1388
1409
node_idx = random .randint (1 , len (self .slots_cache [slot ]) - 1 )
1389
1410
1390
- return self .slots_cache [slot ][node_idx ]
1411
+ try :
1412
+ return self .slots_cache [slot ][node_idx ]
1413
+ except IndexError :
1414
+ return self .slots_cache [slot ][0 ]
1391
1415
1392
1416
def get_nodes_by_server_type (self , server_type ):
1393
1417
"""
@@ -1859,6 +1883,7 @@ def __init__(
1859
1883
cluster_error_retry_attempts : int = 3 ,
1860
1884
reinitialize_steps : int = 5 ,
1861
1885
lock = None ,
1886
+ retry : Optional ["Retry" ] = None ,
1862
1887
** kwargs ,
1863
1888
):
1864
1889
""" """
@@ -1884,6 +1909,7 @@ def __init__(
1884
1909
if lock is None :
1885
1910
lock = threading .Lock ()
1886
1911
self ._lock = lock
1912
+ self .retry = retry
1887
1913
1888
1914
def __repr__ (self ):
1889
1915
""" """
@@ -2016,8 +2042,9 @@ def send_cluster_commands(
2016
2042
stack ,
2017
2043
raise_on_error = raise_on_error ,
2018
2044
allow_redirections = allow_redirections ,
2045
+ attempts_count = self .cluster_error_retry_attempts - retry_attempts ,
2019
2046
)
2020
- except (ClusterDownError , ConnectionError ) as e :
2047
+ except (ClusterDownError , ConnectionError , TimeoutError ) as e :
2021
2048
if retry_attempts > 0 :
2022
2049
# Try again with the new cluster setup. All other errors
2023
2050
# should be raised.
@@ -2027,7 +2054,7 @@ def send_cluster_commands(
2027
2054
raise e
2028
2055
2029
2056
def _send_cluster_commands (
2030
- self , stack , raise_on_error = True , allow_redirections = True
2057
+ self , stack , raise_on_error = True , allow_redirections = True , attempts_count = 0
2031
2058
):
2032
2059
"""
2033
2060
Send a bunch of cluster commands to the redis cluster.
@@ -2082,9 +2109,11 @@ def _send_cluster_commands(
2082
2109
redis_node = self .get_redis_connection (node )
2083
2110
try :
2084
2111
connection = get_connection (redis_node , c .args )
2085
- except ConnectionError :
2086
- # Connection retries are being handled in the node's
2087
- # Retry object. Reinitialize the node -> slot table.
2112
+ except (ConnectionError , TimeoutError ) as e :
2113
+ if self .retry and isinstance (e , self .retry ._supported_errors ):
2114
+ backoff = self .retry ._backoff .compute (attempts_count )
2115
+ if backoff > 0 :
2116
+ time .sleep (backoff )
2088
2117
self .nodes_manager .initialize ()
2089
2118
if is_default_node :
2090
2119
self .replace_default_node ()
0 commit comments