Skip to content

Commit f51e8a5

Browse files
committed
update approach
1 parent bc91967 commit f51e8a5

File tree

6 files changed

+42
-80
lines changed

6 files changed

+42
-80
lines changed

pymongo/asynchronous/pool.py

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
from 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)

pymongo/asynchronous/server.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,6 @@ async def reset(self, service_id: Optional[ObjectId] = None) -> None:
9191
"""Clear the connection pool."""
9292
await self.pool.reset(service_id)
9393

94-
async def backoff(self, service_id: Optional[ObjectId] = None) -> None:
95-
"""Set the connection pool in backoff mode."""
96-
await self.pool.backoff(service_id)
97-
9894
async def close(self) -> None:
9995
"""Clear the connection pool and stop the monitor.
10096

pymongo/asynchronous/topology.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -897,17 +897,13 @@ async def _handle_error(self, address: _Address, err_ctx: _ErrorContext) -> None
897897
if not self._settings.load_balanced:
898898
await self._process_change(ServerDescription(address, error=error))
899899

900-
if err_ctx.completed_handshake:
901-
# Clear the pool.
902-
await server.reset(service_id)
903-
# "When a client marks a server Unknown from `Network error when
904-
# reading or writing`_, clients MUST cancel the hello check on
905-
# that server and close the current monitoring connection."
906-
server._monitor.cancel_check()
907-
return
908-
909-
# Set the pool into backoff mode.
910-
await server.backoff(service_id)
900+
# Clear the pool.
901+
await server.reset(service_id)
902+
# "When a client marks a server Unknown from `Network error when
903+
# reading or writing`_, clients MUST cancel the hello check on
904+
# that server and close the current monitoring connection."
905+
server._monitor.cancel_check()
906+
return
911907

912908
async def handle_error(self, address: _Address, err_ctx: _ErrorContext) -> None:
913909
"""Handle an application error.

pymongo/synchronous/pool.py

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
from pymongo.errors import ( # type:ignore[attr-defined]
4747
AutoReconnect,
4848
ConfigurationError,
49+
ConnectionFailure,
4950
DocumentTooLarge,
5051
ExecutionTimeout,
5152
InvalidOperation,
@@ -938,12 +939,6 @@ def update_is_writable(self, is_writable: Optional[bool]) -> None:
938939
for _socket in self.conns:
939940
_socket.update_is_writable(self.is_writable) # type: ignore[arg-type]
940941

941-
def backoff(self, service_id: Optional[ObjectId] = None) -> None:
942-
# Mark the pool as in backoff.
943-
# TODO: how to handle load balancers?
944-
self._backoff += 1
945-
# TODO: emit a message.
946-
947942
def reset(
948943
self, service_id: Optional[ObjectId] = None, interrupt_connections: bool = False
949944
) -> None:
@@ -1104,9 +1099,14 @@ def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> Connect
11041099

11051100
conn.authenticate()
11061101
# Catch KeyboardInterrupt, CancelledError, etc. and cleanup.
1107-
except BaseException:
1102+
except BaseException as e:
11081103
with self.lock:
11091104
self.active_contexts.discard(conn.cancel_context)
1105+
# Enter backoff mode and reconnect on establishment failure.
1106+
if isinstance(e, ConnectionFailure):
1107+
self._backoff += 1
1108+
# TODO: emit a message about backoff.
1109+
return self.connect(handler)
11101110
conn.close_conn(ConnectionClosedReason.ERROR)
11111111
raise
11121112

@@ -1408,20 +1408,6 @@ def checkin(self, conn: Connection) -> None:
14081408
# Pool.reset().
14091409
if self.stale_generation(conn.generation, conn.service_id):
14101410
close_conn = True
1411-
# If in backoff state, check the conn's readiness.
1412-
elif self._backoff:
1413-
# Set a 1ms read deadline and attempt to read 1 byte from the connection.
1414-
# Expect it to block for 1ms then return a deadline exceeded error. If it
1415-
# returns any other error, the connection is not usable, so return false.
1416-
# If it doesn't return an error and actually reads data, the connection is
1417-
# also not usable, so return false.
1418-
conn.conn.get_conn.settimeout(0.001)
1419-
close_conn = True
1420-
try:
1421-
conn.conn.get_conn.read(1)
1422-
except Exception as _:
1423-
# TODO: verify the exception
1424-
close_conn = False
14251411
else:
14261412
conn.update_last_checkin_time()
14271413
conn.update_is_writable(bool(self.is_writable))
@@ -1452,8 +1438,8 @@ def _perished(self, conn: Connection) -> bool:
14521438
:class:`~pymongo.errors.AutoReconnect` exceptions on server
14531439
hiccups, etc. We only check if the socket was closed by an external
14541440
error if it has been > 1 second since the socket was checked into the
1455-
pool, to keep performance reasonable - we can't avoid AutoReconnects
1456-
completely anyway.
1441+
pool, or we are in backoff mode, to keep performance reasonable -
1442+
we can't avoid AutoReconnects completely anyway.
14571443
"""
14581444
idle_time_seconds = conn.idle_time_seconds()
14591445
# If socket is idle, open a new one.
@@ -1464,8 +1450,11 @@ def _perished(self, conn: Connection) -> bool:
14641450
conn.close_conn(ConnectionClosedReason.IDLE)
14651451
return True
14661452

1467-
if self._check_interval_seconds is not None and (
1468-
self._check_interval_seconds == 0 or idle_time_seconds > self._check_interval_seconds
1453+
check_interval_seconds = self._check_interval_seconds
1454+
if self._backoff:
1455+
check_interval_seconds = 0
1456+
if check_interval_seconds is not None and (
1457+
check_interval_seconds == 0 or idle_time_seconds > check_interval_seconds
14691458
):
14701459
if conn.conn_closed():
14711460
conn.close_conn(ConnectionClosedReason.ERROR)

pymongo/synchronous/server.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,6 @@ def reset(self, service_id: Optional[ObjectId] = None) -> None:
9191
"""Clear the connection pool."""
9292
self.pool.reset(service_id)
9393

94-
def backoff(self, service_id: Optional[ObjectId] = None) -> None:
95-
"""Set the connection pool in backoff mode."""
96-
self.pool.backoff(service_id)
97-
9894
def close(self) -> None:
9995
"""Clear the connection pool and stop the monitor.
10096

pymongo/synchronous/topology.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -895,17 +895,13 @@ def _handle_error(self, address: _Address, err_ctx: _ErrorContext) -> None:
895895
if not self._settings.load_balanced:
896896
self._process_change(ServerDescription(address, error=error))
897897

898-
if err_ctx.completed_handshake:
899-
# Clear the pool.
900-
server.reset(service_id)
901-
# "When a client marks a server Unknown from `Network error when
902-
# reading or writing`_, clients MUST cancel the hello check on
903-
# that server and close the current monitoring connection."
904-
server._monitor.cancel_check()
905-
return
906-
907-
# Set the pool into backoff mode.
908-
server.backoff(service_id)
898+
# Clear the pool.
899+
server.reset(service_id)
900+
# "When a client marks a server Unknown from `Network error when
901+
# reading or writing`_, clients MUST cancel the hello check on
902+
# that server and close the current monitoring connection."
903+
server._monitor.cancel_check()
904+
return
909905

910906
def handle_error(self, address: _Address, err_ctx: _ErrorContext) -> None:
911907
"""Handle an application error.

0 commit comments

Comments
 (0)