logrotate (USR1 signal) restarts uvicorn worker
See original GitHub issueDiscussed in https://github.com/encode/uvicorn/discussions/1558
<div type='discussions-op-text'>Originally posted by wonjoonSeol-WS July 7, 2022 I Use FastAPI+Uvicorn+Gunicorn and issuing USR1 signal to uvicorn worker restarts the worker.
I believe the issue is to do with uvicorn worker not implementing signal handler - github.com/encode/uvicorn/blob/….
def init_signals(self) -> None:
# Reset signals so Gunicorn doesn't swallow subprocess return codes
# other signals are set up by Server.install_signal_handlers()
# See: https://github.com/encode/uvicorn/issues/894
for s in self.SIGNALS:
signal.signal(s, signal.SIG_DFL)
It converts it to signal.SIG_DFL.
SIGUSR1 and SIGUSR2 both have the default action Term – the process is terminated. (unix.stackexchange.com/questions/38589/…)
According to a similar issue page, https://github.com/encode/uvicorn/issues/896#issuecomment-762624434 yinkh changed init_signals to gunicorn worker’s original will not restart the worker.
I also notice that Uvicorn only handles two signals:
HANDLED_SIGNALS = (
signal.SIGINT, # Unix signal 2. Sent by Ctrl+C.
signal.SIGTERM, # Unix signal 15. Sent by `kill <pid>`.
)
I would be more than happy to contribute to uvicorn, if I can change the worker init_signals function to do the same with gunicorn worker?
def init_signals(self):
# reset signaling
for s in self.SIGNALS:
signal.signal(s, signal.SIG_DFL)
# init new signaling
signal.signal(signal.SIGQUIT, self.handle_quit)
signal.signal(signal.SIGTERM, self.handle_exit)
signal.signal(signal.SIGINT, self.handle_quit)
signal.signal(signal.SIGWINCH, self.handle_winch)
signal.signal(signal.SIGUSR1, self.handle_usr1)
signal.signal(signal.SIGABRT, self.handle_abort)
# Don't let SIGTERM and SIGUSR1 disturb active requests
# by interrupting system calls
signal.siginterrupt(signal.SIGTERM, False)
signal.siginterrupt(signal.SIGUSR1, False)
if hasattr(signal, 'set_wakeup_fd'):
signal.set_wakeup_fd(self.PIPE[1])
```</div>
Issue Analytics
- State:
- Created a year ago
- Comments:6 (6 by maintainers)
Top GitHub Comments
Steps to reproduce
Minimal Fastapi code
Run with gunicorn
gunicorn -w 1 -k uvicorn.workers.UvicornWorker --pid /tmp/log/gunicorn.pid main:app
Set up logging
Set up logrotate
/etc/logrotate.d/uvicorn-test
Send USR1 signal
kill -USR1 $(cat /tmp/log/gunicorn.pid)
or force logrotatesudo logrotate --force /etc/logrotate.d/uvicorn-test
We can see the process is terminated:
Proposal: Change uvicorn/worker.py to the following code:
Re-run test
Process is no longer terminated, logrotates fine and log.txt is still appended after the file rotation.
Clearly this change only impacts Uvicorn’s gunicorn worker class. i.e.
kill -USR1 pid
after running it with uvicorn only (uvicorn --host 0.0.0.0 --port 8000 main:app
), the worker is still terminated even with the work above.But
USR1: Reopen the log files
behaviour is only documented on Gunicorn and undocumented on Uvicorn master (yet) so this could be expected behaviour(?). Otherwise we would need to create an async version ofhandle_usr1
method inserver.py
.Btw, we can’t simply fully copy gunicorn’s init_signals method as suggested by yinkh in https://github.com/encode/uvicorn/issues/896#issuecomment-762624434
As the below signals are not compatible with asyncio.
We would end up with errors like below.
Testing other signals such as TTIN, TTOU, QUIT, INT, TERM on gunicorn’s signal handling page (https://docs.gunicorn.org/en/stable/signals.html) seem to work fine.
I have no idea if these two lines from gunicorn worker base class https://github.com/benoitc/gunicorn/blob/027f04b4b4aee4f50b980a7158add0feaf4c1b29/gunicorn/workers/base.py#L185 are needed though. It seem to work fine without it. So I excluded it for now:
If there is no problem with the above change. I’d like to make a PR. I don’t seem to have permission to push a branch now though.
Thanks.
Closed by #1565.
It will be available on the next
uvicorn
release. We are on 0.18.3 at the moment of this writing.