Repository navigation
fix(upgrade-smoke): stop an instance only once Solid Queue has started (#177) - #242
Merged
Merged
Conversation
#177) Solid Queue's Puma plugin loses a stop that reaches the container before its supervisor and the dispatcher, worker and scheduler have trapped signals: Puma waits for the supervisor without a bound and the engine kills the container at the end of the grace period (exit 137). The harness stopped an instance right after the first /up answer, inside that window. stop_and_snapshot now waits (up to 30 s, then stops anyway and says so) for the three "Started" log lines, and a non-graceful stop prints how long it took and the container's last 20 log lines. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…art is a known limit (#177) release-and-deploy.md: the stop rule of the proof, its two new constants, the accepted limit for an operator, and #177 in the sources. README.md's Self-hosting section says plainly that a stop within about 1-2 s of a start can hang until the grace period and be killed, and that nothing is lost. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…oof (#177) Implementation review: spec 031 FR-5 allows no warning that lets the job pass, so the queue-start wait raises a failure naming the missing processes and the log instead of stopping anyway. The area doc and README say the window opens when the app is ready, not at the container's start, that a killed process is not a power loss, and that a running job can be retried; line references and the sightings are corrected. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Three lessons (a shutdown race needs a trigger aimed at its window; a proceed-on-timeout fallback broke spec 031 FR-5; name the trigger moment of every measurement) and how to run bin/upgrade-smoke on a local build. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #177
What was wrong
The upgrade proof stops each instance it boots and compares databases only after a graceful stop (0 or 143). It sometimes got 137: the container was killed at the end of the 30 s grace period. Sightings: runs 42131733342 (PR #165), 42124366255 (
main) and 42658829910 (PR #234, the upgrade instance stopped 2 s after its boot began), allImage (linux/amd64).Cause (reproduced locally, arm64 Docker, image built from this branch)
A start-up race in Solid Queue's Puma plugin (gem 1.7.0; 1.7.2 doesn't change it). Puma's
after_bootedforks the Solid Queue supervisor; on a stop,after_stoppedsends it INT and then callsProcess.waitwith no bound.Stopping Solid Queue...beforeStarted Supervisor(fork), and noterminated gracefullyafter.shutdown_timeoutand sends QUIT. A stop then takes 5-6 s (4-12 s at 0.3 CPU, past the 10 s default ofdocker compose stop).The harness stopped an instance at its first
/upanswer (polled every 2 s), which lands in those windows by chance, more often on a slower runner.Measured before the fix, same image:
/uppoll, idle / job queued / under load / after a second boot (18 stops)/upanswer, polled every 50 ms (15 stops)The fix (smoke side; no app change, no spec: maintainer's ruling)
Upgrade::Harness#stop_and_snapshotfirst waits (wait_for_queue: up to 15 log reads 2 s apart, 28 s) for theStartedline of each of Solid Queue's dispatcher, worker and scheduler, then stops. On timeout it fails (spec 031 FR-5 allows no warning that lets the job pass): the failure names the missing processes and quotes the container's last 20 log lines, and the instance isn't stopped. (The approved brief said proceed on timeout; the implementation review found that breaks FR-5, so this is a deviation, on purpose.)bin/image-smokeandbin/backup-smokeonlyrm -ftheir containers and never stop gracefully, so they need no wait.docker compose stop/ Kamal when the stop arrives within about 1-2 s of the app becoming ready. The maintainer ruled it a known limit: no app-side fix, nothing filed upstream. It is stated indocs/architecture/release-and-deploy.md(accepted limits, An upgrade, constants table, Flaky: Image (linux/amd64) smoke fails with exit 137 at container shutdown #177 in Sources) and in README's Self-hosting section: nothing committed is lost (SQLite's write-ahead log recovers), a job running at that moment can be retried at/admin/jobs.Proof
spec/script/upgrade/harness_spec.rb: waits for all three processes before the stop, fails naming what is missing without stopping, prints the stop's duration and last log lines);FakeEngine's default log now carries the threeStartedlines. 139 examples green inspec/script/upgradeandspec/smoke_scripts_spec.rb; the suite has 2860 examples./up, then for the threeStartedlines in the log, polling every 50 ms, thendocker stop -t 30): 30 of 30 graceful, exit 143, 0.4-0.9 s; 15 of 15 on 0.3 CPU, exit 143, 1.9-6.5 s.bin/upgrade-smokeagainst the locally built image as previous and current (--previous-ref HEAD): 5 of 5 passed (41-78 s), none stopping anyway or failing.Implementation review (Opus, read-only; no spec, so recorded here)
Verdict: ISSUES FOUND, no Critical; all fixed in
7c225321. Findings: the note-and-continue path broke spec 031 FR-5 (now a failure); the docs said three sightings by count and tied the operator window to the container's start rather than the app being ready; the constants table's line references were stale; "as after a power loss" undersold a kill and ignored a running job; "30 s" was 28 s of sleeps;JOB_CONCURRENCY> 1 isn't covered (now a comment onQUEUE_PROCESSES); the first example wouldn't catchall?becomingany?(it does now). Coverage confirmed: every stop that compares databases goes throughstop_and_snapshot(previous, upgraded, restored, next); every release tag locks the samesolid_queueandpuma, so the previous release's log has the same lines; the failure's 20 log lines carry no email, card or location (the seeds run throughdocker exec, and the stopped instances serve only/up,/registration/newand/session/new).Owed to the maintainer
65590894(the fix),d86e7870(docs),7c225321(review fixes),a6ea6d7c(lessons and memory).🤖 Generated with Claude Code