66import uuid
77from abc import abstractmethod
88from concurrent .futures import ThreadPoolExecutor
9- from typing import TYPE_CHECKING
9+ from typing import TYPE_CHECKING , Optional
1010from typing import Union , List , Dict
1111
1212from ravendb .exceptions .exceptions import (
@@ -77,6 +77,16 @@ def __init__(self, topology: Topology):
7777 self .fastest_records = [0 ] * len (topology .nodes )
7878 self .fastest : Union [None , int ] = None
7979 self .speed_test_mode = 0
80+ self .unlikely_everyone_faulted_choice_index : Optional [int ] = 0
81+
82+ @property
83+ def node_when_everyone_marked_as_faulted (self ) -> CurrentIndexAndNode :
84+ index = self .unlikely_everyone_faulted_choice_index
85+ self .unlikely_everyone_faulted_choice_index = (self .unlikely_everyone_faulted_choice_index + 1 ) % len (
86+ self .nodes
87+ )
88+
89+ return CurrentIndexAndNode (index , self .nodes [index ])
8090
8191 def __enter__ (self ):
8292 return self
@@ -94,6 +104,9 @@ def __init__(self, topology: Topology, thread_pool: ThreadPoolExecutor):
94104 def topology (self ) -> Topology :
95105 return self .__state .topology
96106
107+ def node_is_available (self , index : int ) -> bool :
108+ return self .__state .failures [index ] == 0
109+
97110 def on_failed_request (self , node_index : int ) -> None :
98111 state = self .__state
99112 if node_index < 0 or node_index >= len (state .failures ):
@@ -117,16 +130,10 @@ def on_update_topology(self, topology: Topology, force_update: bool = False) ->
117130
118131 def get_requested_node (self , node_tag : str ) -> CurrentIndexAndNode :
119132 state = self .__state
120- state_failures = state .failures
121133 server_nodes = state .nodes
122- length = min (len (server_nodes ), len (state_failures ))
123- for i in range (length ):
134+ for i in range (len (server_nodes )):
124135 if server_nodes [i ].cluster_tag == node_tag :
125- if state_failures [i ] == 0 and server_nodes [i ].url :
126- return CurrentIndexAndNode (i , server_nodes [i ])
127- raise RequestedNodeUnavailableException (
128- f"Requested node { node_tag } is currently unavailable, please try again later."
129- )
136+ return CurrentIndexAndNode (i , server_nodes [i ])
130137
131138 if len (state .nodes ) == 0 :
132139 raise DatabaseDoesNotExistException ("There are no nodes in the topology at all" )
@@ -142,7 +149,7 @@ def get_preferred_node_internal(cls, state: NodeSelector.__NodeSelectorState) ->
142149 server_nodes = state .nodes
143150 length = min (len (server_nodes ), len (state_failures ))
144151 for i in range (length ):
145- if state_failures [0 ] == 0 and server_nodes [ i ]. url :
152+ if state_failures [0 ] == 0 :
146153 return CurrentIndexAndNode (i , server_nodes [i ])
147154 return cls .unlikely_everyone_faulted_choice (state )
148155
@@ -154,11 +161,11 @@ def get_preferred_node_with_topology(self) -> CurrentIndexAndNodeAndEtag:
154161
155162 @staticmethod
156163 def unlikely_everyone_faulted_choice (state : NodeSelector .__NodeSelectorState ) -> CurrentIndexAndNode :
157- # if there are all marked as failed, we'll chose the first
164+ # if there are all marked as failed, we'll chose the next (the one in CurrentNodeIndex)
158165 # one so the user will get an error (or recover :-) )
159166 if len (state .nodes ) == 0 :
160167 raise DatabaseDoesNotExistException ("There are no nodes in the topology at all" )
161- return CurrentIndexAndNode ( 0 , state .nodes [ 0 ])
168+ return state .node_when_everyone_marked_as_faulted
162169
163170 def get_node_by_session_id (self , session_id : int ) -> CurrentIndexAndNode :
164171 state = self .__state
@@ -289,20 +296,34 @@ def __init__(self, current_index: int, current_node: ServerNode, etag: int):
289296class NodeStatus :
290297 def __init__ (
291298 self ,
299+ name : str ,
292300 connected : bool ,
293301 error_details : str ,
294302 last_send : datetime .datetime ,
295303 last_reply : datetime .datetime ,
296304 last_sent_message : str ,
297305 last_matching_index : int ,
298306 ):
307+ self .name = name
299308 self .connected = connected
300309 self .error_details = error_details
301310 self .last_send = last_send
302311 self .last_reply = last_reply
303312 self .last_sent_message = last_sent_message
304313 self .last_matching_index = last_matching_index
305314
315+ def __str__ (self ):
316+ return (
317+ "NodeStatus{"
318+ f"name='{ self .name } '"
319+ f", connected={ self .connected } "
320+ f", errorDetails={ self .error_details } "
321+ f", lastSend={ self .last_send } "
322+ f", lastReply={ self .last_reply } "
323+ f", lastSentMessage={ self .last_sent_message } "
324+ "}"
325+ )
326+
306327
307328class RaftCommand :
308329 @abstractmethod
0 commit comments