What
The last must-fix item on #216: a runner stops by draining instead of killing the jobs it has.
Before this, SIGTERM ended the process mid-step. A build that was nearly done was
thrown away, and its run sat unclaimed until the lease expired, so somebody else
started it again from nothing. With --jobs, that is N jobs lost instead of one.
cmd/vole/runner.goturnsSIGINT/SIGTERMinto a context cancellation, prints one line saying it is draining, and restores the default disposition (signal.Stop) so a second signal ends the process at once. A stuck drain should not needSIGKILL.- Slots stop leasing immediately. A job in flight keeps running and reports as
usual;
servechecks the context at the top of its loop, so nothing new is picked up. - The parked lease is the hard part. A resident slot waits up to 25s for the
server’s reply, and that read belongs to the session’s decoder — nothing inside the
runner can interrupt it. Cancelling the wait means ending the connection
(
hangup, aSIGTERMto thesshprocess, deliberately taken outside the session’s mutex, which the parked goroutine holds). That costs nothing: a slot parked on a lease is holding no job. The slot then exits cleanly instead of trying to reconnect.
What it does not do
The packaged unit already allows 60 seconds between SIGTERM and SIGKILL
(TimeoutStopSec=60s), so a build longer than that is killed anyway. Rather than
change the unit’s judgement for everybody, docs/runner.md has a new “Stopping a
runner” section and docs/deploy/runner.md shows the drop-in:
sudo systemctl edit vole-runner # [Service] / TimeoutStopSec=30min
Tests
ci/agent/slot_test.go:
TestAParkedLeaseEndsWhenTheRunnerStops: a lease on a session that never answers returnserrStoppedwhen the context is cancelled, rather than sitting out its window (guarded by a 5s timeout so a regression fails instead of hanging).TestAStoppedSlotTakesNoWork: a slot told to stop before it leases anything does not lease.
The signal plumbing itself is a CLI concern with no seam worth faking; it is three
lines of select, and the cancellation path it triggers is what the above covers.
Verification
gofmt -l ., go vet ./..., go test -tags sqlite_fts5 ./... clean, and
vole workflow run verify ok (format, vet, build, licenses, govulncheck,
packages, deployment files, links, go test -race -count=1 ./...).
Where #216 stands
Every item on the must-fix list is now in: namespace locks and the trim timer (#217),
--jobs (#218), one build per digest (#219), and this.
Two things are still unproven, and I would not close the issue on them:
- End to end. Nobody has watched
--jobs 4run four real jobs against an instance with a container runtime, or watched four sessions on the runners page. The session half and the slot half are tested separately; the join is a machine. --jobs 2 --onceleases at most two jobs and exits. Implemented (each slot returns after its one job, andRunwaits for all of them), exercised only indirectly.
Refs #216 (does not close it).