-
-
Notifications
You must be signed in to change notification settings - Fork 16.7k
Description
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'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 Flask itself.
Environment:
- Python version: 3.9.7
- Flask version: 2.3.3