Replace forked processes that fail to boot#760
Open
sapandiwakar wants to merge 1 commit into
Open
Conversation
sapandiwakar
force-pushed
the
fix/751-recover-unready-forks
branch
from
July 20, 2026 08:06
ef70db9 to
b5aee2b
Compare
sapandiwakar
marked this pull request as ready for review
July 20, 2026 08:20
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.
What
Track each forked process until its boot callbacks finish. If a child does not report readiness within a configurable startup timeout, terminate it and let the existing reap-and-replacement path replace that configured process.
This applies only to fork mode. The default startup timeout is five minutes.
Why
ForkSupervisorcurrently replaces children only whenwaitpid2(..., WNOHANG)reports that they exited. A child that remains alive while blocked during boot or registration is never returned bywaitpid2, so the supervisor can retain an unusable child indefinitely without replacing it.For a dispatcher, this means the process can remain present while never reaching its polling loop. Due scheduled jobs then remain in
solid_queue_scheduled_executionsinstead of being moved to ready executions.This addresses the concrete recovery gap observed in #751. It does not attempt to detect a process that successfully booted and later stopped making progress.
Reproduction
A deterministic local reproduction is to block a dispatcher in an existing startup hook:
Start Solid Queue in fork mode with a dispatcher configured.
On
main, the dispatcher child remains alive but never reaches its polling loop. Because it has not exited,waitpid2(..., WNOHANG)does not return its PID and the supervisor never replaces it.On this branch, the behavior can be observed quickly by temporarily lowering the timeout:
The supervisor logs the startup timeout, terminates that dispatcher PID, and starts its replacement. The replacement also times out while the hook remains blocked, demonstrating that each configured slot is continuously supervised.
The regression test uses the same failure shape by blocking a worker during registration. It verifies that the stalled child is terminated and replaced while a healthy sibling keeps the same PID.
How
A timed-out child is killed rather than gracefully terminated because a process blocked during boot cannot reach its run loop to complete a graceful stop.
Tests
Relates to #751.