gunicorn
gunicorn copied to clipboard
AsyncToSync error with Flask Async, Gunicorn and Gevent
When using the Async options for Flask, running an application with an asynchronous teardown_request function, using Gunicorn with Gevent as the WSGI server, there is a race condition where a lot of simultaneous requests results in the error "You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly.".
I originally filed this bug against Flask, they told me "[...] that's an issue with Gevent, Gunicorn, or asgiref.AsyncToSync, not Flask. The application layer of WSGI has no control over this."
I've attached a reproduction. To reproduce the bug:
- Unzip the zip to a directory.
- Install the requirements using
pip install -r requirements.txt. - Run the application using
gunicorn -b 0.0.0.0:8080 app:app --worker-class=gevent -w 2 --threads 2. - In another shell, run
./lots-of-curls.sh.
The stack trace I see is
Traceback (most recent call last):
File "/home/sam/.pyenv/versions/3.9.7/envs/flask-test-3.9.7/lib/python3.9/site-packages/gunicorn/workers/base_async.py", line 55, in handle
self.handle_request(listener_name, req, client, addr)
File "/home/sam/.pyenv/versions/3.9.7/envs/flask-test-3.9.7/lib/python3.9/site-packages/gunicorn/workers/ggevent.py", line 128, in handle_request
super().handle_request(listener_name, req, sock, addr)
File "/home/sam/.pyenv/versions/3.9.7/envs/flask-test-3.9.7/lib/python3.9/site-packages/gunicorn/workers/base_async.py", line 108, in handle_request
respiter = self.wsgi(environ, resp.start_response)
File "/home/sam/.pyenv/versions/3.9.7/envs/flask-test-3.9.7/lib/python3.9/site-packages/flask/app.py", line 2213, in __call__
return self.wsgi_app(environ, start_response)
File "/home/sam/.pyenv/versions/3.9.7/envs/flask-test-3.9.7/lib/python3.9/site-packages/flask/app.py", line 2206, in wsgi_app
ctx.pop(error)
File "/home/sam/.pyenv/versions/3.9.7/envs/flask-test-3.9.7/lib/python3.9/site-packages/flask/ctx.py", line 401, in pop
self.app.do_teardown_request(exc)
File "/home/sam/.pyenv/versions/3.9.7/envs/flask-test-3.9.7/lib/python3.9/site-packages/flask/app.py", line 2038, in do_teardown_request
self.ensure_sync(func)(exc)
File "/home/sam/.pyenv/versions/3.9.7/envs/flask-test-3.9.7/lib/python3.9/site-packages/asgiref/sync.py", line 209, in __call__
raise RuntimeError(
RuntimeError: You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly.
If you run without gevent, using the command line gunicorn -b 0.0.0.0:8080 app:app -w 2 --threads 2 the issue does not manifest.
If you remove all the & characters from the end of the lines in lots-of-curls.sh, so the curl statements run serially rather than in parallel, no issue shows up so this appears to definitely be a race condition of some sort.
It is possible this is a bug in another component in the stack, but I just don't have clear enough knowledge of how the responsibilities are assigned to know which component is at fault, apologies if the bug is not in Gunicorn itself.
Environment:
- Python version: 3.9.7
I also ran into this issue with:
- asgiref 3.8.1
- Flask[async] 3.0.2
- Gevent 24.2.1
- Gunicorn 21.2.0
On Python 3.11.2
no activity since awhile. closing feel free to create a new ticket if needed.
note: asgi doesn't work with gevent to my knowledge.
@benoitc I'm baffled why you'd close this. There's been no activity because no one from the project team has looked at it. I just pulled down https://github.com/benoitc/gunicorn/files/12614245/reproduction.zip, updated the requirements.txt with the latest version of all the packages, and was easily able to reproduce this issue.
@SamStephens this issues have been opened 1 year ago... I'm closing all old issues at the moment that didn't get any real activity since the last 3 months. Others will be closed later in the month. New one can be created if needed. I For this one I also commented, I think the issue is using the async keyword inside gevent , this is not supported (you need to check with the gevent project). Also fyi the thread parameter doesn't work with the gevent worker.