mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-28 23:57:58 +09:00
Plan 76 milestone E2, drift/bug row 4. 45 hand-rolled setInterval sites across 35 files, with `visibilitychange` handled in exactly one of them and an in-flight guard in exactly one of them — the documented poller-stampede shape. Two failures every hand-rolled `setInterval(load, 5000)` shares: - it starts a request every 5s whether or not the previous one returned. A slow endpoint does not slow the poller down, it makes it stack; each extra request makes the server slower, which makes the next overlap more likely. This is what a DevTools capture of /domains showed as a wall of pending rows. - it keeps polling a tab nobody is looking at, where browsers throttle timers unevenly anyway, so it is not even a reliable refresh. utils/pollScheduler.js holds the rules as a plain object so they are testable without a DOM (the repo has no jsdom — this follows formState.js/useForm). hooks/usePolling.js is the React binding, and useServerQuery gains `refetchInterval` built on the same scheduler, so there is one implementation rather than a second one for query users. Two design points worth stating, since both differ from what the sites did: - the next tick is armed by the previous run *finishing*, not by setInterval. With setInterval the gap is measured tick-to-tick regardless of how long the work took, so slow work silently turns a 5s gap into no gap. The in-flight guard therefore protects the out-of-band paths — refresh() and the visibility catch-up — rather than the schedule, and the test says so instead of implying a guard the schedule can no longer exercise. - the callback is held in a ref, so an inline arrow re-created each render does not restart the timer. That is the usual reason a hand-rolled poller runs far faster than its stated interval. Migrated four as reference implementations, per the plan's "migrate a representative flow before broader adoption": DeployPill (which had worked all of this out by hand and loses ~20 lines), MonitorsSummary, appdetail/LogsTab, and ProcessListPanel. MonitorsSummary keeps its `refreshKey` prop as an explicit out-of-band refresh rather than folding it into the schedule. Ratchet: check-frontend-boundaries.mjs gains LEGACY_POLLERS with exact per-file counts, now 41 sites in 31 files, so every future cleanup must shrink it and a new bare setInterval fails lint. Genuine non-fetching timers (clock ticks, countdowns) are listed too rather than pattern-matched away — they are harmless but the exact count is what makes the ratchet trustworthy. Gate: npm test (72 passed, 11 of them new), npm run lint (0 errors; 954 warnings, one fewer than the 955 baseline), npm run build clean, boundary check green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>