Skip to content

Commit 20511f8

Browse files
committed
Ensure pool connection is released in acquire when cancelled
Closes MagicStack#547 When wait_for is cancelled, there is a chance that the waited task has already been completed, leaving the connection looking like it is in use. This fix ensures that the connection is returned to the pool in this situation. For context, see: https://bugs.python.org/issue37658 MagicStack#467
1 parent 2c99beb commit 20511f8

File tree

1 file changed

+17
-3
lines changed

1 file changed

+17
-3
lines changed

asyncpg/pool.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -605,15 +605,29 @@ async def _acquire_impl():
605605
ch._timeout = timeout
606606
return proxy
607607

608+
def _release_leaked_connection(fut):
609+
if not fut.cancelled():
610+
asyncio.ensure_future(self.release(fut.result()))
611+
608612
if self._closing:
609613
raise exceptions.InterfaceError('pool is closing')
610614
self._check_init()
611615

616+
acquire_fut = asyncio.ensure_future(_acquire_impl())
612617
if timeout is None:
613-
return await _acquire_impl()
618+
return await acquire_fut
614619
else:
615-
return await asyncio.wait_for(
616-
_acquire_impl(), timeout=timeout)
620+
try:
621+
return await asyncio.wait_for(
622+
acquire_fut, timeout=timeout)
623+
except asyncio.CancelledError:
624+
# Ensure connection is marked as not in use.
625+
# The cancellation may have raced the acquire, leading
626+
# to the acquire completing but the wait_for to be
627+
# cancelled.
628+
# See: https://bugs.python.org/issue37658
629+
acquire_fut.add_done_callback(_release_leaked_connection)
630+
raise
617631

618632
async def release(self, connection, *, timeout=None):
619633
"""Release a database connection back to the pool.

0 commit comments

Comments
 (0)