fix(#5543): fire-and-forget commit_session_memory + drain-safe managed stop
Move the 1-5s commit_session_memory() extraction off the POST /api/session/new request thread (was blocking + New Chat) into a fire-and-forget daemon thread, guarded by the existing per-session in-flight serialization. Data-loss prevention on managed stop: - server.py installs a SIGTERM handler that requests an orderly stop so serve_forever()'s finally block runs drain_all_on_shutdown() (the default SIGTERM disposition terminated without unwinding, losing in-flight commits). - session_lifecycle: bounded self-unregistering background-commit registry; a _draining flag so no NEW worker starts once the drain begins (the inline generation-drain commits any late arrival instead); a 30s overall drain deadline so a stuck worker cannot hang the stop path forever. Co-authored-by: luperrypf <luperrypf@users.noreply.github.com>
This commit is contained in:
1 parent
04717ad305
commit
19d00e3586
4 files changed
+539
-17
No files matched your search
@@ -688,6 +688,36 @@ def main() -> None:
|
||||
print(f' Remote access: ssh -N -L {PORT}:127.0.0.1:{PORT} <user>@<your-server>', flush=True)
|
||||
print(f' Then open: {scheme}://localhost:{PORT}', flush=True)
|
||||
print('', flush=True)
|
||||
|
||||
# ctl.sh stops the WebUI with SIGTERM. Python's default SIGTERM handler
|
||||
# terminates the process WITHOUT unwinding the try/finally around
|
||||
# serve_forever(), so drain_all_on_shutdown() (which flushes in-flight
|
||||
# fire-and-forget memory commits) would never run on the normal managed
|
||||
# stop. Install a handler that requests an orderly shutdown so
|
||||
# serve_forever() returns and the existing `finally` block drains cleanly.
|
||||
#
|
||||
# httpd.shutdown() blocks until serve_forever() has exited and MUST NOT be
|
||||
# called from the thread running serve_forever() (it would deadlock), so we
|
||||
# dispatch it from a short-lived helper thread. The handler is idempotent
|
||||
# and guards against double-shutdown (e.g. repeated SIGTERM/SIGINT).
|
||||
_shutdown_requested = threading.Event()
|
||||
|
||||
def _request_shutdown(signum, _frame):
|
||||
if _shutdown_requested.is_set():
|
||||
return
|
||||
_shutdown_requested.set()
|
||||
threading.Thread(
|
||||
target=httpd.shutdown,
|
||||
name="webui-sigterm-shutdown",
|
||||
daemon=True,
|
||||
).start()
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGTERM, _request_shutdown)
|
||||
except (ValueError, OSError):
|
||||
# Not on the main thread (e.g. embedded/test harness); skip handler.
|
||||
logger.debug("Could not install SIGTERM handler", exc_info=True)
|
||||
|
||||
try:
|
||||
httpd.serve_forever()
|
||||
finally:
|
||||
|
||||
Reference in new issue
Block a user