Skip to content

Commit 5a1e7cb

Browse files
committed
PYTHON-5044 - Successive AsyncMongoClients on a single loop always timeout on server selection
1 parent e4d8449 commit 5a1e7cb

File tree

4 files changed

+24
-12
lines changed

4 files changed

+24
-12
lines changed

pymongo/asynchronous/mongo_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,8 @@ async def close(self) -> None:
15651565
# TODO: PYTHON-1921 Encrypted MongoClients cannot be re-opened.
15661566
await self._encrypter.close()
15671567
self._closed = True
1568+
# Yield to the asyncio event loop so all executor tasks properly exit after cancellation
1569+
await asyncio.sleep(0)
15681570

15691571
if not _IS_SYNC:
15701572
# Add support for contextlib.aclosing.

pymongo/network_layer.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -267,18 +267,25 @@ async def async_receive_data(
267267
else:
268268
read_task = create_task(_async_receive(sock, length, loop)) # type: ignore[arg-type]
269269
tasks = [read_task, cancellation_task]
270-
done, pending = await asyncio.wait(
271-
tasks, timeout=timeout, return_when=asyncio.FIRST_COMPLETED
272-
)
273-
for task in pending:
274-
task.cancel()
275-
if pending:
276-
await asyncio.wait(pending)
277-
if len(done) == 0:
278-
raise socket.timeout("timed out")
279-
if read_task in done:
280-
return read_task.result()
281-
raise _OperationCancelled("operation cancelled")
270+
try:
271+
done, pending = await asyncio.wait(
272+
tasks, timeout=timeout, return_when=asyncio.FIRST_COMPLETED
273+
)
274+
for task in pending:
275+
task.cancel()
276+
if pending:
277+
await asyncio.wait(pending)
278+
if len(done) == 0:
279+
raise socket.timeout("timed out")
280+
if read_task in done:
281+
return read_task.result()
282+
raise _OperationCancelled("operation cancelled")
283+
except asyncio.CancelledError:
284+
for task in tasks:
285+
task.cancel()
286+
await asyncio.wait(tasks)
287+
raise
288+
282289
finally:
283290
sock.settimeout(sock_timeout)
284291

pymongo/periodic_executor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ def close(self, dummy: Any = None) -> None:
7575
callback; see monitor.py.
7676
"""
7777
self._stopped = True
78+
if self._task:
79+
self._task.cancel()
7880

7981
async def join(self, timeout: Optional[int] = None) -> None:
8082
if self._task is not None:

pymongo/synchronous/mongo_client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,7 @@ def close(self) -> None:
15591559
# TODO: PYTHON-1921 Encrypted MongoClients cannot be re-opened.
15601560
self._encrypter.close()
15611561
self._closed = True
1562+
# Yield to the asyncio event loop so all executor tasks properly exit after cancellation
15621563

15631564
if not _IS_SYNC:
15641565
# Add support for contextlib.closing.

0 commit comments

Comments
 (0)