In investigating #3656 I found the initial problem to be that when running in a container, Gunicorn will kill the worker process if a thread doesn't respond for 30 seconds by default. I fixed that by making the timeout match the application session timeout, but it revealed another issue.
Given the function below (from the ticket), if you open the query tool and run:
SELECT 1; SELECT fails_after(30);
the async query actually blocks for 30 seconds in the cur.execute() call in execute_async() in connection.py (line 968). This causes the entire app to hang (watch the dashboard requests pile up in pending state in the network tab of the browser dev tools).
If you run just the second SELECT, it works as expected, as does running something like: