fix(selfhost): phase-align the cron scheduler to wall-clock boundaries - #8061
Conversation
Cloudflare's own */2 * * * * cron trigger fires exactly on wall-clock 2-minute boundaries (:00, :02, :04, ...), which every minute-gated job in enqueueScheduledJobs (minute % 10 === 0, minute === 0, minute % 30 === 0 -- all even) depends on to ever run. The self-host entrypoint's plain setInterval instead ticked every CRON_INTERVAL_MS from whatever moment the container booted, with no relation to wall-clock boundaries -- and since the interval evenly divides an hour, that locks every tick to a FIXED minute parity for the container's entire lifetime. A container booting in an odd minute then ticks ONLY on odd minutes forever, so refresh-registry, ops-alerts, sweep-watchdog, backfill-registered-repos, and both reconciliation sweeps silently NEVER fire. Confirmed live on edge-nl-01: the app container booted at :49 (odd) and ran 3+ hours of on-schedule ~2-minute ticks with zero occurrences of any minute-gated job, while the unconditional every-tick sweep ran normally the whole time. Phase-align the first tick to the next true wall-clock boundary (computed from epoch, itself minute-aligned) with a one-shot setTimeout, then hand off to setInterval from that aligned moment.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-22 17:44:01 UTC
Review summary Nits — 5 non-blocking
Concerns raised — review before merging
📋 Copy for AI agents — paste into your coding agentDecision drivers
Context & advisory signals — never blocks the verdict
Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8061 +/- ##
===========================================
+ Coverage 91.81% 100.00% +8.18%
===========================================
Files 731 1 -730
Lines 74862 2 -74860
Branches 22973 1 -22972
===========================================
- Hits 68733 2 -68731
+ Misses 5034 0 -5034
+ Partials 1095 0 -1095
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Summary
*/2 * * * *cron trigger fires exactly on wall-clock 2-minute boundaries, which every minute-gated job inenqueueScheduledJobs(minute % 10 === 0,minute === 0,minute % 30 === 0-- all even) depends on to ever run.setIntervalinstead ticked everyCRON_INTERVAL_MSfrom whatever moment the container booted, with no relation to wall-clock boundaries. Since the interval evenly divides an hour, that locks every tick to a fixed minute parity for the container's entire lifetime -- a container that boots in an odd minute ticks only on odd minutes forever, sorefresh-registry,ops-alerts,sweep-liveness-watchdog,backfill-registered-repos, and both reconciliation sweeps silently never fire.What Changed
src/selfhost/cron-alignment.ts: puredelayToNextWallClockBoundaryMs(nowMs, intervalMs)helper, 100% branch-covered.src/server.ts: the cron setup now phase-aligns its first tick to the next true wall-clock boundary with a one-shotsetTimeout, then hands off tosetIntervalfrom that aligned moment so every subsequent tick keeps landing on the same boundaries Cloudflare's cron would.Test plan
test/unit/selfhost-cron-alignment.test.tscovering both branches (mid-cycle boot, exact-boundary boot) plus a real-timestamp regression case matching the observed production boot moment.npm run test:ci-- green (one unrelated, pre-existing flaky test inagent-sdk-driver.test.tstimed out under parallel load; confirmed passing in isolation, unrelated to this change).npm run typecheck-- clean.npm audit --audit-level=moderate-- same 13 pre-existing transitive vulnerabilities as before this change (sharp/miniflare/wrangler chain), unrelated.