gunicorn icon indicating copy to clipboard operation
gunicorn copied to clipboard

AsyncToSync error with Flask Async, Gunicorn and Gevent

Open SamStephens opened this issue 2 years ago • 1 comments

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

SamStephens avatar Sep 14 '23 23:09 SamStephens

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

owenlamont avatar Apr 04 '24 07:04 owenlamont

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 avatar Aug 06 '24 21:08 benoitc

@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 avatar Aug 06 '24 22:08 SamStephens

@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.

benoitc avatar Aug 07 '24 06:08 benoitc