Cleaner server shutdowns:
- Remove a workaround for Sanic server not always terminating cleanly - Terminate worker threads before server stop - Silent closing of watching WebSocket attempted to open while shutting down
This commit is contained in:
parent
26addb2f7b
commit
33db2c01b4
@ -119,8 +119,12 @@ async def watch(req, ws):
|
|||||||
# Send updates
|
# Send updates
|
||||||
while True:
|
while True:
|
||||||
await ws.send(await q.get())
|
await ws.send(await q.get())
|
||||||
|
except RuntimeError as e:
|
||||||
|
if str(e) == "cannot schedule new futures after shutdown":
|
||||||
|
return # Server shutting down, drop the WebSocket
|
||||||
|
raise
|
||||||
finally:
|
finally:
|
||||||
del watching.pubsub[uuid]
|
watching.pubsub.pop(uuid, None) # Remove whether it got added yet or not
|
||||||
|
|
||||||
|
|
||||||
def subscribe(uuid, ws):
|
def subscribe(uuid, ws):
|
||||||
|
@ -43,14 +43,16 @@ async def main_start(app, loop):
|
|||||||
app.ctx.threadexec = ThreadPoolExecutor(
|
app.ctx.threadexec = ThreadPoolExecutor(
|
||||||
max_workers=workers, thread_name_prefix="cista-ioworker"
|
max_workers=workers, thread_name_prefix="cista-ioworker"
|
||||||
)
|
)
|
||||||
await watching.start(app, loop)
|
watching.start(app, loop)
|
||||||
|
|
||||||
|
|
||||||
@app.after_server_stop
|
# Sanic sometimes fails to execute after_server_stop, so we do it before instead (potentially interrupting handlers)
|
||||||
|
@app.before_server_stop
|
||||||
async def main_stop(app, loop):
|
async def main_stop(app, loop):
|
||||||
quit.set()
|
quit.set()
|
||||||
await watching.stop(app, loop)
|
watching.stop(app)
|
||||||
app.ctx.threadexec.shutdown()
|
app.ctx.threadexec.shutdown()
|
||||||
|
logger.debug("Cista worker threads all finished")
|
||||||
|
|
||||||
|
|
||||||
@app.on_request
|
@app.on_request
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import signal
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
@ -12,14 +11,6 @@ def run(*, dev=False):
|
|||||||
"""Run Sanic main process that spawns worker processes to serve HTTP requests."""
|
"""Run Sanic main process that spawns worker processes to serve HTTP requests."""
|
||||||
from .app import app
|
from .app import app
|
||||||
|
|
||||||
# Set up immediate exit on Ctrl+C for faster termination
|
|
||||||
def signal_handler(signum, frame):
|
|
||||||
print("\nReceived interrupt signal, exiting immediately...")
|
|
||||||
os._exit(0)
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
|
||||||
signal.signal(signal.SIGTERM, signal_handler)
|
|
||||||
|
|
||||||
url, opts = parse_listen(config.config.listen)
|
url, opts = parse_listen(config.config.listen)
|
||||||
# Silence Sanic's warning about running in production rather than debug
|
# Silence Sanic's warning about running in production rather than debug
|
||||||
os.environ["SANIC_IGNORE_PRODUCTION_WARNING"] = "1"
|
os.environ["SANIC_IGNORE_PRODUCTION_WARNING"] = "1"
|
||||||
|
@ -440,7 +440,7 @@ def watcher_poll(loop):
|
|||||||
quit.wait(0.1 + 8 * dur)
|
quit.wait(0.1 + 8 * dur)
|
||||||
|
|
||||||
|
|
||||||
async def start(app, loop):
|
def start(app, loop):
|
||||||
global rootpath
|
global rootpath
|
||||||
config.load_config()
|
config.load_config()
|
||||||
rootpath = config.config.path
|
rootpath = config.config.path
|
||||||
@ -454,6 +454,6 @@ async def start(app, loop):
|
|||||||
app.ctx.watcher.start()
|
app.ctx.watcher.start()
|
||||||
|
|
||||||
|
|
||||||
async def stop(app, loop):
|
def stop(app):
|
||||||
quit.set()
|
quit.set()
|
||||||
app.ctx.watcher.join()
|
app.ctx.watcher.join()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user