@@ -820,8 +820,7 @@ def __init__(
820820 async def ready (self ) -> None :
821821 # Take the lock to avoid the race condition described in PYTHON-2699.
822822 async with self .lock :
823- # Do not set the pool as ready if in backoff.
824- if self ._backoff :
823+ if self .state == PoolState .BACKOFF :
825824 return
826825 if self .state != PoolState .READY :
827826 self .state = PoolState .READY
@@ -847,14 +846,11 @@ async def _reset(
847846 pause : bool = True ,
848847 service_id : Optional [ObjectId ] = None ,
849848 interrupt_connections : bool = False ,
850- from_server_description : bool = False ,
851849 ) -> None :
852850 old_state = self .state
853851 async with self .size_cond :
854852 if self .closed :
855853 return
856- if from_server_description and self .state == PoolState .BACKOFF :
857- return
858854 # Clear the backoff amount.
859855 self ._backoff = 0
860856 if self .opts .pause_enabled and pause and not self .opts .load_balanced :
@@ -954,16 +950,12 @@ async def update_is_writable(self, is_writable: Optional[bool]) -> None:
954950 _socket .update_is_writable (self .is_writable ) # type: ignore[arg-type]
955951
956952 async def reset (
957- self ,
958- service_id : Optional [ObjectId ] = None ,
959- interrupt_connections : bool = False ,
960- from_server_description : bool = False ,
953+ self , service_id : Optional [ObjectId ] = None , interrupt_connections : bool = False
961954 ) -> None :
962955 await self ._reset (
963956 close = False ,
964957 service_id = service_id ,
965958 interrupt_connections = interrupt_connections ,
966- from_server_description = from_server_description ,
967959 )
968960
969961 async def reset_without_pause (self ) -> None :
@@ -1044,27 +1036,30 @@ async def remove_stale_sockets(self, reference_generation: int) -> None:
10441036 self .requests -= 1
10451037 self .size_cond .notify ()
10461038
1047- def _handle_connection_error (self , error : BaseException , phase : str ) -> None :
1039+ async def _handle_connection_error (self , error : BaseException , phase : str ) -> None :
10481040 # Handle system overload condition for non-sdam pools.
10491041 # Look for an AutoReconnect or NetworkTimeout error.
10501042 # If found, set backoff and add error labels.
10511043 if self .is_sdam or type (error ) not in (AutoReconnect , NetworkTimeout ):
10521044 return
10531045 error ._add_error_label ("SystemOverloadedError" ) # type:ignore[attr-defined]
10541046 error ._add_error_label ("RetryableError" ) # type:ignore[attr-defined]
1055- self .backoff ()
1047+ await self .backoff ()
10561048
1057- def backoff (self ) -> None :
1049+ async def backoff (self ) -> None :
10581050 """Set/increase backoff mode."""
1059- self ._backoff += 1
1060- backoff_duration_sec = _backoff (self ._backoff )
1061- backoff_duration_ms = int (backoff_duration_sec * 1000 )
1062- if self .state != PoolState .BACKOFF :
1063- self .state = PoolState .BACKOFF
1064- if self .enabled_for_cmap :
1065- assert self .opts ._event_listeners is not None
1066- self .opts ._event_listeners .publish_pool_backoff (self .address , backoff_duration_ms )
1067- self ._backoff_connection_time = backoff_duration_sec + time .monotonic ()
1051+ async with self .lock :
1052+ self ._backoff += 1
1053+ backoff_duration_sec = _backoff (self ._backoff )
1054+ backoff_duration_ms = int (backoff_duration_sec * 1000 )
1055+ if self .state != PoolState .BACKOFF :
1056+ self .state = PoolState .BACKOFF
1057+ if self .enabled_for_cmap :
1058+ assert self .opts ._event_listeners is not None
1059+ self .opts ._event_listeners .publish_pool_backoff (
1060+ self .address , self ._backoff , backoff_duration_ms
1061+ )
1062+ self ._backoff_connection_time = backoff_duration_sec + time .monotonic ()
10681063
10691064 # Log the pool backoff message.
10701065 if self .enabled_for_logging and _CONNECTION_LOGGER .isEnabledFor (logging .DEBUG ):
@@ -1074,6 +1069,7 @@ def backoff(self) -> None:
10741069 clientId = self ._client_id ,
10751070 serverHost = self .address [0 ],
10761071 serverPort = self .address [1 ],
1072+ attempt = self ._backoff ,
10771073 durationMS = backoff_duration_ms ,
10781074 reason = _verbose_connection_error_reason (ConnectionClosedReason .POOL_BACKOFF ),
10791075 error = ConnectionClosedReason .POOL_BACKOFF ,
@@ -1136,7 +1132,7 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
11361132 error = ConnectionClosedReason .ERROR ,
11371133 )
11381134 if context ["has_created_socket" ]:
1139- self ._handle_connection_error (error , "handshake" )
1135+ await self ._handle_connection_error (error , "handshake" )
11401136 if isinstance (error , (IOError , OSError , * SSLErrors )):
11411137 details = _get_timeout_details (self .opts )
11421138 _raise_connection_failure (self .address , error , timeout_details = details )
@@ -1148,9 +1144,11 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
11481144 self .active_contexts .discard (tmp_context )
11491145 if tmp_context .cancelled :
11501146 conn .cancel_context .cancel ()
1147+ has_completed_hello = False
11511148 try :
11521149 if not self .is_sdam :
11531150 await conn .hello ()
1151+ has_completed_hello = True
11541152 self .is_writable = conn .is_writable
11551153 if handler :
11561154 handler .contribute_socket (conn , completed_handshake = False )
@@ -1160,7 +1158,8 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
11601158 except BaseException as e :
11611159 async with self .lock :
11621160 self .active_contexts .discard (conn .cancel_context )
1163- self ._handle_connection_error (e , "hello" )
1161+ if not has_completed_hello :
1162+ await self ._handle_connection_error (e , "hello" )
11641163 await conn .close_conn (ConnectionClosedReason .ERROR )
11651164 raise
11661165
0 commit comments