--- /dev/null
+.. change::
+ :tags: bug, postgresql
+ :tickets: 10717
+
+ Adjusted the asyncpg dialect such that when the ``terminate()`` method is
+ used to discard an invalidated connection, the dialect will first attempt
+ to gracefully close the conneciton using ``.close()`` with a timeout, if
+ the operation is proceeding within an async event loop context only. This
+ allows the asyncpg driver to attend to finalizing a ``TimeoutError``
+ including being able to close a long-running query server side, which
+ otherwise can keep running after the program has exited.
from __future__ import annotations
+import asyncio
import collections
import decimal
import json as _py_json
self.await_(self._connection.close())
def terminate(self):
- self._connection.terminate()
+ if util.concurrency.in_greenlet():
+ # in a greenlet; this is the connection was invalidated
+ # case.
+ try:
+ # try to gracefully close; see #10717
+ # timeout added in asyncpg 0.14.0 December 2017
+ self.await_(self._connection.close(timeout=2))
+ except asyncio.TimeoutError:
+ # in the case where we are recycling an old connection
+ # that may have already been disconnected, close() will
+ # fail with the above timeout. in this case, terminate
+ # the connection without any further waiting.
+ # see issue #8419
+ self._connection.terminate()
+ else:
+ # not in a greenlet; this is the gc cleanup case
+ self._connection.terminate()
self._started = False
@staticmethod
awaitable.close()
+def in_greenlet() -> bool:
+ current = _concurrency_shim.getcurrent()
+ return isinstance(current, _concurrency_shim._AsyncIoGreenlet)
+
+
def await_only(awaitable: Awaitable[_T]) -> _T:
"""Awaits an async function in a sync method.