fix(webhook-agent-ingest): prevent duplicate cron alarm firings - #5523
Merged
pandemicsyn merged 2 commits intoAug 26, 2026
Conversation
Fix a race condition where cron alarms could fire twice for the same occurrence if the alarm triggered slightly early due to jitter. The fix involves: - Anchoring the next scheduled time calculation on the previously targeted occurrence (`nextScheduledAt`) if it is still in the future, ensuring the scheduler always advances to the subsequent occurrence. - Changing the jitter implementation to be strictly positive (0-30s) to prevent the alarm from being scheduled before its target time. - Updating `computeNextCronTime` to accept an optional reference date. - Adding unit tests to verify that the next occurrence is strictly after the provided reference time. Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
…update Address static review findings on the previous duplicate-alarm fix: - updateConfig()'s reactivate/cron-change branch cleared nextScheduledAt before rescheduling, since it previously carried a pending (never fired) occurrence from the old config that would incorrectly get skipped by the new anchoring logic. - Use an explicit Number.isNaN check instead of relying on incidental NaN comparison behavior when validating the stored nextScheduledAt. - Add cron.test.ts coverage for far-future anchors across day boundaries and for an Invalid Date passed as the reference time. Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Contributor
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryReviewed the scheduled-cron double-fire fix in TriggerDO alarm scheduling and Files Reviewed (3 files)
Reviewed by grok-4.6 · Input: 61K · Output: 12.9K · Cached: 386.7K Review guidance: REVIEW.md from base branch |
iscekic
approved these changes
Aug 26, 2026
pandemicsyn
deleted the
session/agent_49f591d0-2c90-4239-a2c6-d0b5c1135455
branch
August 26, 2026 18:36
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
Fixes a bug where a Cloud Agent scheduled webhook trigger could fire twice for the same cron occurrence — one invocation up to 30s early, followed by a duplicate ~1 minute later (as seen in the screenshot on the "Scheduled requests" table, e.g.
12:09and12:10).Root cause, in
services/webhook-agent-ingest/src/dos/TriggerDO.ts:scheduleNextAlarm()applied jitter in the range±30swhen scheduling the Durable Object alarm for the next cron occurrence. Negative jitter meant the alarm could fire before its target time.cronerreturned the same occurrence again, causing it to be scheduled (and fired) a second time.Fix:
computeNextCronTime()(services/webhook-agent-ingest/src/util/cron.ts) now accepts an optionalafterreferenceDate, passed through tocroner'snextRun(after).scheduleNextAlarm()jitter changed from±30sto0–30s(delay-only, never negative), so the alarm can no longer fire ahead of its target occurrence.scheduleNextAlarm()also anchors onmax(now, previousTarget)when a valid futurenextScheduledAtexists, so even a slightly-early alarm fire (clock skew, platform-level timing) correctly advances to the next occurrence instead of recomputing the one just handled.updateConfig()'s "reactivated or cron expression changed" branch now clears the inheritednextScheduledAtbefore rescheduling — otherwise a pending (never-fired) occurrence carried over from the old config would get treated as "already consumed" by the new anchoring logic, silently skipping a valid, possibly sooner occurrence under the new cron expression/timezone.No architectural changes — this is a targeted fix within the existing Durable Object alarm-based scheduling design.
Verification
computeNextCronTime()'s newafterparameter, including early-fire, far-future-anchor, and invalid-date cases, plus reasoning through all call sites ofscheduleNextAlarm()(initial configure, normal alarm fire, DST-retry branch, and config update) via a static review pass.Visual Changes
N/A
Reviewer Notes
TriggerDO.scheduleNextAlarm()andalarm()— worth tracing through all four call sites (initialconfigure(),updateConfig()'s reactivate/cron-change branch, the normalalarm()fire path, and the DSTalarmRetryretry path) to confirm the anchoring behaves correctly in each.nextScheduledAtis dual-purpose (UI display value and internal "occurrence already handled" marker); theupdateConfig()fix clears it explicitly where it would otherwise be misread as "already fired" for a config that hasn't fired yet.TriggerDO's Durable Object alarm scheduling directly (onlycron.tsunit tests); the DO-level behavior (config update reschedule, DST retry) was verified by code reading rather than an integration test.