Document daemon shutdown ownership

This commit is contained in:
Nick Sweeting 2026-08-31 22:26:06 -07:00
parent 4fc4df9da8
commit c1dda18443
No known key found for this signature in database
4 changed files with 21 additions and 0 deletions

View File

@ -54,6 +54,11 @@ RUNNER_DAEMON_ENV = "ARCHIVEBOX_RUNNER_DAEMON"
def _exit_daemon_runner_on_signal(sig: signal.Signals) -> None:
# A supervised `archivebox run --daemon` is intentionally a disposable
# child. If it receives SIGINT/SIGTERM directly, exit with the conventional
# signal status so supervisord treats it as an unexpected worker death and
# restarts only the runner. The parent `archivebox server` owns supervisord
# shutdown and must not be pulled down by a killed daemon worker.
os._exit(128 + int(sig))

View File

@ -48,6 +48,11 @@ def _exit_on_migration_interrupt():
os.write(sys.stderr.fileno(), _migration_interrupt_message().encode())
except Exception:
pass
# Django's migration executor can catch or delay normal exceptions while
# unwinding transactions. Use the real process exit path after printing
# the recovery command so Ctrl+C/SIGTERM during auto-migrations does not
# leave `archivebox server` apparently hung after the user asked it to
# stop. Migrations are atomic, so this does not record partial progress.
os._exit(130)
try:

View File

@ -261,6 +261,10 @@ class CrawlRunner:
def _request_abort_from_signal(self, _sig: signal.Signals) -> None:
if os.environ.get("ARCHIVEBOX_RUNNER_DAEMON") == "1":
# The daemon runner is owned by supervisord, not by the interactive
# CLI foreground flow. A direct signal to this child should be short
# and unambiguous: exit non-zero immediately so supervisord restarts
# the runner, while the parent server and supervisord stay alive.
os._exit(128 + int(_sig))
already_requested = self._signal_abort_requested
self._signal_abort_requested = True

View File

@ -275,6 +275,10 @@ def RUNNER_WORKER():
"command": _shell_join(archivebox_cmd("run", "--daemon")),
"autostart": "false",
"autorestart": "true",
# Mark the long-lived runner child so its own SIGINT/SIGTERM path exits
# with a signal code instead of running foreground server cleanup. That
# keeps "kill just archivebox run --daemon" as a worker restart event;
# only killing the parent server or supervisord should stop the stack.
"environment": 'PYTHONUNBUFFERED="1",COLUMNS="200",ARCHIVEBOX_RUNNER_DAEMON="1"',
"stopasgroup": "true",
"killasgroup": "true",
@ -288,6 +292,9 @@ RUNNER_ONCE_WORKER = lambda args, name="worker_runner_once": {
**RUNNER_WORKER(),
"name": name,
"command": _shell_join(archivebox_cmd("run", "--no-stdin", *args)),
# One-shot foreground jobs are awaited by the command that launched them,
# so they keep the normal cooperative shutdown path instead of the daemon
# marker that tells supervisord to restart an independently killed worker.
"environment": 'PYTHONUNBUFFERED="1",COLUMNS="200"',
"autorestart": "false",
"stopwaitsecs": "1",