From 93e1208cbc5e5ab6e0e35827cc6ea5b9b8809746 Mon Sep 17 00:00:00 2001 From: Saty <5752427+SatyPardus@users.noreply.github.com> Date: Sun, 18 Oct 2026 04:45:11 +0200 Subject: [PATCH] Worker drains on stop instead of re-queuing work; update.sh no longer waits for it Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RNwtXPYqFu3oKaaXwYwNWW --- deploy/togetherdecomp-worker.service | 3 ++- deploy/update.sh | 16 ++++------------ src/server/app.js | 17 ++++++++++++----- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/deploy/togetherdecomp-worker.service b/deploy/togetherdecomp-worker.service index 95b5ffc..821ad53 100644 --- a/deploy/togetherdecomp-worker.service +++ b/deploy/togetherdecomp-worker.service @@ -13,7 +13,8 @@ ExecStart=/opt/node/bin/node --disable-warning=ExperimentalWarning /opt/together Restart=on-failure # Restarting re-queues whatever was being verified. RestartSec=5 -TimeoutStopSec=15 +# Stopping waits for the running verification or merge to finish. +TimeoutStopSec=1200 Environment=NODE_ENV=production # Hardening. The process needs: its data dir, the docker socket, git. diff --git a/deploy/update.sh b/deploy/update.sh index 757a578..3e3e0c7 100644 --- a/deploy/update.sh +++ b/deploy/update.sh @@ -13,7 +13,6 @@ set -eu SRC=$(cd "${1:?usage: update.sh }" && pwd) BASE=/opt/togetherdecomp-releases LINK=/opt/togetherdecomp -JOBS=/var/lib/togetherdecomp/jobs NODE=/opt/node/bin/node REL="$BASE/$(date +%Y%m%d-%H%M%S)" @@ -66,17 +65,10 @@ else systemctl restart togetherdecomp.service sleep 2 systemctl is-active --quiet togetherdecomp.service || { journalctl -u togetherdecomp -n 20 --no-pager; exit 1; } - # A restart re-queues what the worker is doing. Remote checks are cheap to redo (the waiting agent - # still gets its result); a batch verification or merge is waited for, up to 15 minutes. - echo "== restarting the worker once no batch is being verified or merged" - waited=0 - while ls "$JOBS" 2>/dev/null | grep -qE '^(batch|merge)-' && [ "$waited" -lt 900 ]; do - sleep 5 - waited=$((waited + 5)) - done - systemctl restart togetherdecomp-worker.service - sleep 2 - systemctl is-active --quiet togetherdecomp-worker.service || { journalctl -u togetherdecomp-worker -n 20 --no-pager; exit 1; } + # The worker finishes its running verification, merge or check before it stops (no work is + # redone), then starts on the new release. That can take minutes, so don't wait for it here. + echo "== the worker restarts once its running work is done" + systemctl restart --no-block togetherdecomp-worker.service fi # Keep the last three releases for a quick rollback (ln -sfn /opt/togetherdecomp). diff --git a/src/server/app.js b/src/server/app.js index 60145a1..d5241df 100644 --- a/src/server/app.js +++ b/src/server/app.js @@ -720,14 +720,21 @@ export async function startServer(dataDir, { role = 'all' } = {}) { await new Promise((r) => server.listen(ctx.config.listen.port, ctx.config.listen.host, r)); console.log(`clank together coordinator listening on http://${ctx.config.listen.host}:${ctx.config.listen.port} (public url ${ctx.config.publicUrl})`); } - // Quick exit: requests still open (an agent waiting on a remote check can wait for minutes) are - // cut off after a moment; agents retry, and the work itself lives in the database and the worker. + // The web side exits quickly: requests still open (an agent waiting on a remote check can wait + // for minutes) are cut off after a moment; agents retry. The worker drains: it takes no new work + // and exits once its running verifications, merges and checks are done, so nothing is redone. + const busy = () => ctx.runsWorker && (ctx.verifier.running > 0 || ctx.merger.busy || ctx.checks.running > 0); + const exitWhenIdle = () => (busy() ? setTimeout(exitWhenIdle, 1000) : process.exit(0)); const shutdown = () => { for (const stop of stops) stop(); - if (!server) process.exit(0); - server.close(() => process.exit(0)); + if (busy()) console.log('stopping: finishing the running work first'); + if (!server) return exitWhenIdle(); + server.close(); server.closeIdleConnections(); - setTimeout(() => process.exit(0), 3000).unref(); + setTimeout(() => { + server.closeAllConnections(); + exitWhenIdle(); + }, 3000).unref(); }; process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown);