4949from pymongo .errors import ( # type:ignore[attr-defined]
5050 AutoReconnect ,
5151 ConfigurationError ,
52+ ConnectionFailure ,
5253 DocumentTooLarge ,
5354 ExecutionTimeout ,
5455 InvalidOperation ,
@@ -940,12 +941,6 @@ async def update_is_writable(self, is_writable: Optional[bool]) -> None:
940941 for _socket in self .conns :
941942 _socket .update_is_writable (self .is_writable ) # type: ignore[arg-type]
942943
943- async def backoff (self , service_id : Optional [ObjectId ] = None ) -> None :
944- # Mark the pool as in backoff.
945- # TODO: how to handle load balancers?
946- self ._backoff += 1
947- # TODO: emit a message.
948-
949944 async def reset (
950945 self , service_id : Optional [ObjectId ] = None , interrupt_connections : bool = False
951946 ) -> None :
@@ -1108,9 +1103,14 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
11081103
11091104 await conn .authenticate ()
11101105 # Catch KeyboardInterrupt, CancelledError, etc. and cleanup.
1111- except BaseException :
1106+ except BaseException as e :
11121107 async with self .lock :
11131108 self .active_contexts .discard (conn .cancel_context )
1109+ # Enter backoff mode and reconnect on establishment failure.
1110+ if isinstance (e , ConnectionFailure ):
1111+ self ._backoff += 1
1112+ # TODO: emit a message about backoff.
1113+ return await self .connect (handler )
11141114 await conn .close_conn (ConnectionClosedReason .ERROR )
11151115 raise
11161116
@@ -1412,20 +1412,6 @@ async def checkin(self, conn: AsyncConnection) -> None:
14121412 # Pool.reset().
14131413 if self .stale_generation (conn .generation , conn .service_id ):
14141414 close_conn = True
1415- # If in backoff state, check the conn's readiness.
1416- elif self ._backoff :
1417- # Set a 1ms read deadline and attempt to read 1 byte from the connection.
1418- # Expect it to block for 1ms then return a deadline exceeded error. If it
1419- # returns any other error, the connection is not usable, so return false.
1420- # If it doesn't return an error and actually reads data, the connection is
1421- # also not usable, so return false.
1422- conn .conn .get_conn .settimeout (0.001 )
1423- close_conn = True
1424- try :
1425- conn .conn .get_conn .read (1 )
1426- except Exception as _ :
1427- # TODO: verify the exception
1428- close_conn = False
14291415 else :
14301416 conn .update_last_checkin_time ()
14311417 conn .update_is_writable (bool (self .is_writable ))
@@ -1456,8 +1442,8 @@ async def _perished(self, conn: AsyncConnection) -> bool:
14561442 :class:`~pymongo.errors.AutoReconnect` exceptions on server
14571443 hiccups, etc. We only check if the socket was closed by an external
14581444 error if it has been > 1 second since the socket was checked into the
1459- pool, to keep performance reasonable - we can't avoid AutoReconnects
1460- completely anyway.
1445+ pool, or we are in backoff mode, to keep performance reasonable -
1446+ we can't avoid AutoReconnects completely anyway.
14611447 """
14621448 idle_time_seconds = conn .idle_time_seconds ()
14631449 # If socket is idle, open a new one.
@@ -1468,8 +1454,11 @@ async def _perished(self, conn: AsyncConnection) -> bool:
14681454 await conn .close_conn (ConnectionClosedReason .IDLE )
14691455 return True
14701456
1471- if self ._check_interval_seconds is not None and (
1472- self ._check_interval_seconds == 0 or idle_time_seconds > self ._check_interval_seconds
1457+ check_interval_seconds = self ._check_interval_seconds
1458+ if self ._backoff :
1459+ check_interval_seconds = 0
1460+ if check_interval_seconds is not None and (
1461+ check_interval_seconds == 0 or idle_time_seconds > check_interval_seconds
14731462 ):
14741463 if conn .conn_closed ():
14751464 await conn .close_conn (ConnectionClosedReason .ERROR )
0 commit comments