main_process_stop runs BEFORE before_server_stop when interrupted with SIGINT
arsentyev opened this issue · comments
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
The lifecycle diagram in the docs shows that main_process_stop runs AFTER all before_server_stop listeners, but it actually runs BEFORE, at least when the main process was interrupted with SIGINT.
Code snippet
from sanic import Sanic
from sanic.response import json
app = Sanic("my-hello-world-app")
@app.route('/')
async def test(request):
return json({'hello': 'world'})
@app.main_process_stop
def main_process_stop(app, loop):
print ("main_process_stop")
@app.before_server_stop
def before_server_stop(app, loop):
print ("before_server_stop")
if __name__ == '__main__':
app.run(fast=True)
Expected Behavior
[2023-08-25 11:04:24 +0400] [5017] [INFO] Received signal SIGINT. Shutting down.
...
before_server_stop
...
before_server_stop
main_process_stop
How do you run Sanic?
As a script (app.run
or Sanic.serve
)
Operating System
MacOS
Sanic Version
Sanic 23.6.0; Routing 23.6.0
Additional context
The actual output:
[2023-08-25 11:04:24 +0400] [5017] [INFO] Received signal SIGINT. Shutting down.
[2023-08-25 11:04:24 +0400] [5017] [INFO] Server Stopped
main_process_stop
[2023-08-25 11:04:24 +0400] [5029] [INFO] Stopping worker [5029]
...
[2023-08-25 11:04:24 +0400] [5024] [INFO] Stopping worker [5024]
before_server_stop
...
before_server_stop
They are run in different processes and not interdependent. You cannot rely upon the order of them as the OS is responsible for scheduling between them. The events are triggered and executed. You can rely upon the after_server_stop
happening after before_server_stop
as they are in the same process. Similarly, the reloader stop event also happens in it's own process.
This is neither a bug, nor an expected feature.
What are you trying to do that this would matter?
Thank you for explanation. I agree that worker processes are independent but dependent on the main process. Random execution order between workers and main process is not clear from your docs.
Your WorkManager.run
seems to be trying to join
to worker processes and wait for them to complete. But actual multiprocessing.Process.join
is not called, at least, in case of SIGINT. It looks like a bug. And if you want to dive deeper: calling WorkManager.run should wait for worker processes to complete or not?
What are you trying to do to make it matter?
I'm just trying to clean up some resources shared by worker processes. To do this, I need to wait for them to complete (more precisely all request handlers in all processes). Is there a standard way to do this?
I have been playing with this, and I see what you mean now. And, I have what I think is a better solution in play. Will require some tests and cleaning it up, but the PR is here: #2811