From 0daa32e7a79304bc7bd6b89737bf357936b6028e Mon Sep 17 00:00:00 2001 From: Seamus Abshere Date: Sun, 17 Oct 2021 09:33:12 -0400 Subject: [PATCH 1/3] Make sure connection._ready() does not stall on bad file descriptor With @emk We've observed "[Errno 2] No such file or directory" coming from add_writer(). When this happens, nobody notifies _waiter that there has been an exception and this hangs the program. We haven't managed to create a local repo - whenever we (for example) kill -9 a postgres process while an aiopg client is writing to it, we get the intended psycopg2.OperationalError("Connection closed"). Here's the backtrace of the error seen in the wild: Exception in callback Connection._ready() at /root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.7/site-packages/aiopg/connection.py:779 handle: ) at /root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.7/site-packages/aiopg/connection.py:779> Traceback (most recent call last): [... app code ...] File "/usr/lib/python3.7/asyncio/base_events.py", line 566, in run_until_complete self.run_forever() File "/usr/lib/python3.7/asyncio/base_events.py", line 534, in run_forever self._run_once() File "/usr/lib/python3.7/asyncio/base_events.py", line 1771, in _run_once handle._run() File "/usr/lib/python3.7/asyncio/events.py", line 88, in _run self._context.run(self._callback, *self._args) File "/root/.local/share/virtualenvs/app-4PlAip0Q/lib/python3.7/site-packages/aiopg/connection.py", line 838, in _ready self._fileno, self._ready, weak_self # type: ignore File "/usr/lib/python3.7/asyncio/selector_events.py", line 334, in add_writer return self._add_writer(fd, callback, *args) File "/usr/lib/python3.7/asyncio/selector_events.py", line 294, in _add_writer (reader, handle)) File "/usr/lib/python3.7/selectors.py", line 389, in modify self._selector.modify(key.fd, selector_events) FileNotFoundError: [Errno 2] No such file or directory --- aiopg/connection.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/aiopg/connection.py b/aiopg/connection.py index ca32bda0..1648c52e 100755 --- a/aiopg/connection.py +++ b/aiopg/connection.py @@ -834,10 +834,13 @@ def _ready(weak_self: "weakref.ref[Any]") -> None: self._writing = False elif state == psycopg2.extensions.POLL_WRITE: if not self._writing: - self._loop.add_writer( - self._fileno, self._ready, weak_self # type: ignore - ) - self._writing = True + try: + self._loop.add_writer( + self._fileno, self._ready, weak_self # type: ignore + ) + self._writing = True + except Exception as e: + waiter.set_exception(e) elif state == psycopg2.extensions.POLL_ERROR: self._fatal_error( "Fatal error on aiopg connection: " From 4787ba0c5e8c8b0a6615436f9b17e78cac35f334 Mon Sep 17 00:00:00 2001 From: Tom Caruso Date: Tue, 9 Nov 2021 18:38:05 -0500 Subject: [PATCH 2/3] update async_timeout version to match upstream this was causing sub-dependency version mismatch errors when updating to python 3.10 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6cd4ccef..812a4440 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages -install_requires = ["psycopg2-binary>=2.8.4", "async_timeout>=3.0,<4.0"] +install_requires = ["psycopg2-binary>=2.8.4", "async_timeout>=3.0,<5.0"] extras_require = {"sa": ["sqlalchemy[postgresql_psycopg2binary]>=1.3,<1.5"]} From c77349eb9307ef969a544e086cac77dc148366bb Mon Sep 17 00:00:00 2001 From: Seamus Abshere Date: Fri, 7 Jan 2022 17:38:01 -0500 Subject: [PATCH 3/3] Update aiopg/connection.py Co-authored-by: Andrew Svetlov --- aiopg/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiopg/connection.py b/aiopg/connection.py index 1648c52e..dfbec5a7 100755 --- a/aiopg/connection.py +++ b/aiopg/connection.py @@ -839,7 +839,7 @@ def _ready(weak_self: "weakref.ref[Any]") -> None: self._fileno, self._ready, weak_self # type: ignore ) self._writing = True - except Exception as e: + except OSError as e: waiter.set_exception(e) elif state == psycopg2.extensions.POLL_ERROR: self._fatal_error(