49
49
from pymongo .errors import ( # type:ignore[attr-defined]
50
50
AutoReconnect ,
51
51
ConfigurationError ,
52
+ ConnectionFailure ,
52
53
DocumentTooLarge ,
53
54
ExecutionTimeout ,
54
55
InvalidOperation ,
@@ -940,12 +941,6 @@ async def update_is_writable(self, is_writable: Optional[bool]) -> None:
940
941
for _socket in self .conns :
941
942
_socket .update_is_writable (self .is_writable ) # type: ignore[arg-type]
942
943
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
-
949
944
async def reset (
950
945
self , service_id : Optional [ObjectId ] = None , interrupt_connections : bool = False
951
946
) -> None :
@@ -1108,9 +1103,14 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
1108
1103
1109
1104
await conn .authenticate ()
1110
1105
# Catch KeyboardInterrupt, CancelledError, etc. and cleanup.
1111
- except BaseException :
1106
+ except BaseException as e :
1112
1107
async with self .lock :
1113
1108
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 )
1114
1114
await conn .close_conn (ConnectionClosedReason .ERROR )
1115
1115
raise
1116
1116
@@ -1412,20 +1412,6 @@ async def checkin(self, conn: AsyncConnection) -> None:
1412
1412
# Pool.reset().
1413
1413
if self .stale_generation (conn .generation , conn .service_id ):
1414
1414
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
1429
1415
else :
1430
1416
conn .update_last_checkin_time ()
1431
1417
conn .update_is_writable (bool (self .is_writable ))
@@ -1456,8 +1442,8 @@ async def _perished(self, conn: AsyncConnection) -> bool:
1456
1442
:class:`~pymongo.errors.AutoReconnect` exceptions on server
1457
1443
hiccups, etc. We only check if the socket was closed by an external
1458
1444
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.
1461
1447
"""
1462
1448
idle_time_seconds = conn .idle_time_seconds ()
1463
1449
# If socket is idle, open a new one.
@@ -1468,8 +1454,11 @@ async def _perished(self, conn: AsyncConnection) -> bool:
1468
1454
await conn .close_conn (ConnectionClosedReason .IDLE )
1469
1455
return True
1470
1456
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
1473
1462
):
1474
1463
if conn .conn_closed ():
1475
1464
await conn .close_conn (ConnectionClosedReason .ERROR )
0 commit comments