Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Received duplicated signal from Gunicorn #1116

Closed
2 tasks done
Kludex opened this issue Jul 10, 2021 · 13 comments
Closed
2 tasks done

[BUG] Received duplicated signal from Gunicorn #1116

Kludex opened this issue Jul 10, 2021 · 13 comments

Comments

@Kludex
Copy link
Sponsor Member

Kludex commented Jul 10, 2021

Checklist

  • The bug is reproducible against the latest release and/or master.
  • There are no similar issues or pull requests to fix it yet.

Describe the bug

Currently, when we send SIGINT to the gunicorn process that is running with uvicorn workers, the SIGINT is not propagated to the workers. On its turn, a SIGQUIT is sent to them. By the time of this issue, the reason for that decision is unclear, but you can follow this gunicorn issue to understand more.

The mentioned behavior has an effect on uvicorn, that I'm unsure how we should act. The effect is that when running gunicorn with uvicorn workers, the shutdown event doesn't happen. Which, in theory, is fine. As SIGINT and SIGQUIT, should be terminating the worker immediately, but that is not what is in place for standalone uvicorn i.e. SIGNIT triggers the shutdown event.

Well, all of what I mentioned above should actually be a separated issue, but I was not able to solve it without workarounds, that I'm unsure if we want them. The reason for that is the uvicorn worker receiving two signals when we send SIGINT to the gunicorn process, one is SIGINT, which is sent directly to the uvicorn worker and the other is the SIGQUIT which is sent by the gunicorn process.

As you see, even if we fix SIGQUIT by sending SIGINT, the issue is not solved i.e. uvicorn will receive double SIGINT, and we will force exit (via force_exit attribute).

To reproduce

Just create any ASGI app, with a shutdown event:

# test.py
from fastapi import FastAPI

app = FastAPI()

@app.on_event("shutdown")
async def shutdown():
    print("This will not be triggered")

Then run gunicorn with uvicorn workers:

gunicorn -k uvicorn.workers.UvicornWorker test:app

Feel free to press CTRL + C and you'll see this log:

❯ gunicorn -k uvicorn.workers.UvicornWorker test:app
[2021-07-10 20:08:01 +0200] [43323] [INFO] Starting gunicorn 20.1.0
[2021-07-10 20:08:01 +0200] [43323] [INFO] Listening at: http://127.0.0.1:8000 (43323)
[2021-07-10 20:08:01 +0200] [43323] [INFO] Using worker: uvicorn.workers.UvicornWorker
[2021-07-10 20:08:01 +0200] [43325] [INFO] Booting worker with pid: 43325
[2021-07-10 20:08:01 +0200] [43325] [INFO] Started server process [43325]
[2021-07-10 20:08:01 +0200] [43325] [INFO] Waiting for application startup.
[2021-07-10 20:08:01 +0200] [43325] [INFO] Application startup complete.
^C[2021-07-10 20:08:01 +0200] [43323] [INFO] Handling signal: int
[2021-07-10 20:08:01 +0200] [43325] [INFO] Shutting down
[2021-07-10 20:08:01 +0200] [43325] [INFO] Error while closing socket [Errno 9] Bad file descriptor
[2021-07-10 20:08:01 +0200] [43325] [INFO] Finished server process [43325]
[2021-07-10 20:08:01 +0200] [43325] [INFO] Worker exiting (pid: 43325)
[2021-07-10 20:08:01 +0200] [43323] [INFO] Shutting down: Master

As you see, the message on the shutdown event doesn't appear.

Expected behavior

If we want to have consistency with the standalone uvicorn, which I'm really unsure (as it doesn't feel right to wait the process to finish gracefully with SIGINT), then we should match this behavior and gunicorn with uvicorn workers should trigger the shutdown event as well.

In case we decide that the shutdown event should actually not be triggered by standalone uvicorn, then the issue here is no more an issue, and we should stop handling the SIGINT on the server.py. I believe this is unlikely to happen, because of practicality when developing.

Actual behavior

The shutdown event is not triggered running gunicorn with uvicorn workers when SIGINT is sent.

Environment

  • OS / Python / Uvicorn version: Running uvicorn 0.14.0 with CPython 3.8.10 on Linux
  • gunicorn (version 20.1.0)

Additional context

More context can be found on Gitter. Here are some messages, but you can follow the discussion there:
https://gitter.im/encode/community?at=60e3592b457e19611a37a5d6
https://gitter.im/encode/community?at=60e35dd8f862a72a30eeb8dc
https://gitter.im/encode/community?at=60e5966e24f0ae2a244a5e0d

\cc @tomchristie @florimondmanca

@sandys
Copy link

sandys commented Aug 21, 2021

this issue is affecting us as well. Is there a fix that will be done by uvicorn ?

@Kludex
Copy link
Sponsor Member Author

Kludex commented Aug 22, 2021

I'm still waiting for a reply on benoitc/gunicorn#2604.

Do you have any suggestion? We could add a handler for SIGQUIT on the gunicorn worker...

@sandys
Copy link

sandys commented Aug 22, 2021

@Kludex gunicorn is maintaining parity with uwsgi. Both kill off workers + shutdown when SIGINT or SIGQUIT is received. https://uwsgi-docs.readthedocs.io/en/latest/Management.html . in fact in uwsgi 2.1, SIGTERM also shuts down uwsgi. ( https://uwsgi-docs.readthedocs.io/en/latest/ThingsToKnow.html#things-to-know-best-practices-and-issues-read-it)

i think (speculating) that the way the internal code is written in both uwsgi and gunicorn is to kill everything in case of these three signals ( https://github.com/unbit/uwsgi/blob/d58a832c81c2c96ae0f6e72614e1cc47f4b5d332/core/emperor.c#L2000-L2002)

so ultimately i think that uvicorn will need to honor the gunicorn signal behavior (when running as a worker) and not interpret signals directly.

@sapjunior
Copy link

Is there any workaround for this problem? This issue block our project deployment timeline.

@vlad-pro
Copy link

vlad-pro commented Nov 5, 2021

Experiencing the same issue with gunicorn and uvicorn workers on GCP cloudrun platform.

@dmrz
Copy link

dmrz commented Nov 19, 2021

Here is a workaround I am using (is not battle-tested much yet though):

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()


if "gunicorn" in os.environ.get("SERVER_SOFTWARE", ""):
    loop = asyncio.get_event_loop()
    loop.add_signal_handler(signal.SIGQUIT, lambda _: asyncio.create_task(shutdown()))

@Feijo
Copy link

Feijo commented Mar 27, 2022

Any news about it?

@Kludex
Copy link
Sponsor Member Author

Kludex commented Oct 14, 2022

PR is welcome to modify the UvicornWorker.

@adnaanbheda
Copy link
Contributor

@Kludex I see that you had created a PR (#1424), Is it fine if I polish that up and create a PR ?

@Kludex
Copy link
Sponsor Member Author

Kludex commented Oct 16, 2022

Yes. 🙏

@adnaanbheda
Copy link
Contributor

adnaanbheda commented Oct 16, 2022

@Kludex I've tested that PR, it works fine, i.e. the shutdown event is triggered, but shouldn't we use the server's install_signal_handlers to do this instead ?

Adding SIGQUIT as a handled signal in server just works fine as well, what do you think?

server.py

HANDLED_SIGNALS = (
    signal.SIGINT,  # Unix signal 2. Sent by Ctrl+C.
    signal.SIGTERM,  # Unix signal 15. Sent by `kill <pid>`.
    signal.SIGQUIT,
)

EDIT:

#1424 is triggering the shutdown event just fine for SIGINT and shutting down the application, but if we send a SIGQUIT it doesn't handle it. I've created a PR #1709, that does both.

@Kludex
Copy link
Sponsor Member Author

Kludex commented Oct 16, 2022

On my side, I'm not willing to change uvicorn for this, only the UvicornWorker.

@Kludex
Copy link
Sponsor Member Author

Kludex commented Nov 1, 2022

It will be available on Uvicorn 0.20.0.

@Kludex Kludex closed this as completed Nov 1, 2022
@Kludex Kludex added this to the Version 0.20.0 milestone Nov 1, 2022
@br3ndonland br3ndonland mentioned this issue Jun 4, 2023
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants