pool: preserve running jobs across repeated starts - #87
Merged
Merged
Conversation
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.
A repeated recovery delivery could remove a running job's ownership and leave a scheduler unable to finish its plan. The worker called
JobHandler.Startagain for a key it already handled. When a handler rejected that duplicate, failed-start cleanup deleted the original job's saved ownership and payload even though its handler was still running. A local reproduction then showed the scheduler repeatedly failing on that key, never starting the following job or advancing its next planning time.The pool now handles repeated delivery before calling the handler again. A matching delivery keeps the accepted handler running once. Requests carrying a dispatch ID—the identifier for one admitted submission—still validate their original key and payload. A different pending submission or conflicting recovery payload is rejected without replacing the running job. Replaying a completed submission returns its retained outcome without modifying the current job at that key.
Recovery also restores missing ownership or payload records for an accepted handler. One Redis operation checks the active pool generation, node and worker registrations, pending dispatch, existing payload and other owners before restoring only missing entries. It cannot take ownership from another worker. A failed repair leaves the handler and its local record intact so delivery can be retried.
Starts, stops, dispatch settlement, moves and shutdown requeue now share the worker's existing coordination lock. Incoming recovery deliveries check their current destination after waiting for that lock. Internal restoration after a failed move remains able to restart the original handler; this preserves recovery when either ownership removal or publication of the move fails.
No public types, method signatures, stored fields, resource names or Redis migrations change. Existing callers still submit the same job keys and payloads.
JobHandlerdocumentation now states how repeated accepted delivery is handled. Applications adopt the fix by updating Pulse and replacing their workers normally; no ownership reset is required.Validation performed:
go test -race -p 1 ./... -count=1 -timeout=8mpassed in 244.76 seconds.staticcheck ./...passed.The main contracts to inspect are
Worker.startJobandstartJobLockedfor delivery versus internal restoration,acceptRunningJobfor replay identity, andrestoreRunningJobScriptfor conditional ownership repair. The regression tests follow the complete path through a successful subsequent scheduler plan.An independent correctness review rechecked the failed-move and orphan-recovery corrections and found no remaining blocker on the tested four-file patch. CI, dependency review and all CodeQL checks passed on
2b43504ee2865b2d95d361a4c4716767443acf6f. Copilot was requested on that same commit, but could not run because the requesting account had reached its review quota; it left no inline findings.