fix: correlate execute() by id, isolate event hooks, and stop counting manual runs toward maxExecutions#607
Merged
Merged
Conversation
…g manual runs toward maxExecutions Three related bugs in the execution path: - execute() could resolve with a concurrent scheduled run's result instead of its own, because it matched on the generic execution:finished/failed events without checking which execution settled. Both the inline and background tasks now correlate by the execution id they invoked; for background tasks the id is echoed through the task:execute IPC command and the resulting events. - A user execution:finished listener that threw synchronously could reclassify a successful run as failed (emitting execution:failed after execution:finished and overwriting lastRun()), and a rejecting async listener became an unhandled rejection. The runner now isolates the onFinished hook from the task's own try block, and event emission on the inline task catches both throwing and rejecting listeners. - Manual execute() incremented runCount without ever checking maxExecutions, so repeated manual calls could silently stop the task. maxExecutions now counts and is enforced only for scheduled fires; execute() never counts toward it and always runs, even after the task has auto-stopped.
…rrectness # Conflicts: # src/tasks/background-scheduled-task/daemon.ts
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.
Summary
Three related bugs in the runner/task execution path:
execute() could resolve with the wrong run's result. It matched on the generic
execution:finished/execution:failedevents without checking which execution actually settled, so a scheduled fire completing while a manualexecute()call was still in flight could resolve the manual call with the scheduled run's result. BothInlineScheduledTask.execute()andBackgroundScheduledTask.execute()now correlate by the execution id they invoked. For background tasks, the id is generated in the parent, sent through thetask:executeIPC command, threaded into the daemon'sInlineScheduledTask.execute(id)call, and echoed back on the forwarded events.A throwing/rejecting user hook could reclassify a successful run as failed.
onFinishedwas called inside the task's own try block, so a synchronously-throwingexecution:finishedlistener made the runner treat a successful execution as failed (emittingexecution:failedafterexecution:finished, and overwritinglastRun()). An async listener that rejected became an unhandled rejection. The runner now callsonFinishedoutside the task's try block, in its own try/catch routed to the internal error fallback logger, and the inline task's event emission catches both a throwing and a rejecting listener without affecting the task's own outcome.Manual execute() silently drifted toward maxExecutions. It incremented
runCounton every call but never checkedmaxExecutions, so repeated manual calls could push the count past the limit. Per decision:maxExecutions(andrunsLeft()) now only counts scheduled fires. Manualexecute()never counts toward it and is never blocked by it, even after the task has auto-stopped frommaxExecutions.Test plan
npm run buildnpx tsc --noEmitnpm run lintnpm test(741 tests, 100% statements/branches/functions/lines)