Skip to content

Replace ad-hoc cron Scheduler with chained-timeout helper#595

Merged
Brutus5000 merged 1 commit into
developfrom
chore/scheduler-cleanup
May 26, 2026
Merged

Replace ad-hoc cron Scheduler with chained-timeout helper#595
Brutus5000 merged 1 commit into
developfrom
chore/scheduler-cleanup

Conversation

@Brutus5000

@Brutus5000 Brutus5000 commented May 26, 2026

Copy link
Copy Markdown
Member

Summary

  • New tiny helpers in src/backend/cron-jobs/: scheduleRepeating (chained setTimeout, can't overlap, returns a .stop() handle) and warmAll (collects per-action failures via Promise.allSettled, logs each, then throws so the scheduler-level log records the failure).
  • Single CACHE_WARM_INTERVAL_MS constant in constants.js replaces the magic 60 * 59 * 1000 that was duplicated in each crawler. Comment explains the relationship to the 60-minute service cache TTL.
  • The three crawlers (leaderboard, wordpress, clan) shrink to ~15 lines each — just a list of named warmup actions.
  • AppKernel.startCronJobs now registers SIGTERM/SIGINT handlers that call .stop() on every scheduler, so the pending timer is cleared on shutdown (no leaked timers under nodemon or Docker stop).
  • Deletes the EventEmitter-based services/Scheduler.js (22 lines wrapping setInterval for no real benefit).

Net diff: +116 / -196 across 8 files; cron-jobs/ directory goes from ~190 LOC to ~110.

Why

The previous Scheduler class added indirection without value, and each crawler reimplemented the same boilerplate (per-call success/error handlers, magic interval constant, top-level catch). Three concrete bugs/risks the cleanup also fixes:

  1. setInterval could schedule a second warmup before the previous one finished — now impossible because the next timer is only set in finally after the run completes.
  2. No clean shutdown — timer handles were never cleared on signal, so nodemon restarts and Docker stop would leak callbacks until the process exited.
  3. Per-action failure isolation was copy-pasted into every crawler; now lives in one helper and behaves consistently.

This deliberately does not introduce node-cron or a job queue (BullMQ etc.) — neither would solve a real problem here, and both would add deps.

Test plan

  • yarn lint clean
  • yarn test — 71/71 (tests don't exercise startCronJobs, but they do boot AppKernel and would catch import-level breakage)
  • Manual: deploy to test env, confirm [scheduler] leaderboard-cache ok in Xms (or failed) lines appear at startup and roughly every 59 minutes
  • Manual: docker stop the running container, confirm the process exits cleanly (no lingering timer warnings)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved shutdown behavior: background tasks now properly stop when the application receives shutdown signals.
  • Refactor

    • Optimized cache warming operations to run concurrently instead of sequentially, improving overall performance.
    • Introduced configurable cache refresh intervals with intelligent timing designed to refresh cached data before expiry.

Review Change Stack

The EventEmitter-based Scheduler class wrapped setInterval to no real
benefit and each of the three cache crawlers duplicated ~60 lines of
boilerplate around it. This collapses the lot into two small helpers
(scheduleRepeating, warmAll) and a shared interval constant, taking the
cron-jobs/ directory from ~190 lines to ~110.

Behavior changes:
- Chained setTimeout instead of setInterval, so a slow run can't overlap
  with the next one if a warmup ever takes longer than the interval.
- Per-action failures inside a warmup are collected (Promise.allSettled)
  rather than aborting the rest, then re-thrown to surface to the
  scheduler-level log.
- SIGTERM and SIGINT now call .stop() on every active scheduler so the
  pending timer is cleared on shutdown.
- One named constant (CACHE_WARM_INTERVAL_MS) replaces the magic
  `60 * 59 * 1000` repeated in each crawler, with a comment explaining
  the relationship to the 60-minute service cache TTL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1ecff770-992e-4f39-b575-2198ec6c5bbb

📥 Commits

Reviewing files that changed from the base of the PR and between eace251 and 78b6531.

📒 Files selected for processing (8)
  • src/backend/AppKernel.js
  • src/backend/cron-jobs/clanCacheCrawler.js
  • src/backend/cron-jobs/constants.js
  • src/backend/cron-jobs/leaderboardCacheCrawler.js
  • src/backend/cron-jobs/scheduleRepeating.js
  • src/backend/cron-jobs/warmAll.js
  • src/backend/cron-jobs/wordpressCacheCrawler.js
  • src/backend/services/Scheduler.js
💤 Files with no reviewable changes (1)
  • src/backend/services/Scheduler.js

📝 Walkthrough

Walkthrough

The PR replaces a monolithic EventEmitter-based Scheduler class with composable utilities: scheduleRepeating for interval-based async task scheduling with logging, and warmAll for concurrent cache-warming with error aggregation. All three cache crawlers (clan, leaderboard, WordPress) are refactored to use these utilities with a shared interval constant. AppKernel now gracefully stops all schedulers on termination signals.

Changes

Scheduler Replacement and Graceful Shutdown

Layer / File(s) Summary
Core scheduling and warming utilities
src/backend/cron-jobs/scheduleRepeating.js, src/backend/cron-jobs/warmAll.js, src/backend/cron-jobs/constants.js
New scheduleRepeating async scheduler executes functions on intervals with timing and error logs; new warmAll concurrently warms caches via Promise.allSettled and aggregates failures; shared CACHE_WARM_INTERVAL_MS constant (59 minutes) undershoots cache TTL.
Refactor cache crawlers to new scheduler pattern
src/backend/cron-jobs/clanCacheCrawler.js, src/backend/cron-jobs/leaderboardCacheCrawler.js, src/backend/cron-jobs/wordpressCacheCrawler.js
clanCacheCrawler, leaderboardCacheCrawler, and wordpressCacheCrawler all migrate from manual Scheduler construction to scheduleRepeating with warmAll delegation; removes per-entry try/catch logging and simplifies to data-driven warmup functions.
Graceful shutdown on termination signals
src/backend/AppKernel.js
startCronJobs() registers SIGTERM/SIGINT handlers that iterate this.schedulers and call stop() on each, preventing orphaned timeouts after process exit.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 The old Scheduler class takes its final bow,
New utilities dance in harmony now,
scheduleRepeating and warmAll play their part,
While caches refresh with a synchronized heart,
And when signals arrive, the shutdown's so clean! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: replacing a custom EventEmitter-based Scheduler with a simpler chained-timeout helper pattern.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/scheduler-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Brutus5000

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Brutus5000 Brutus5000 merged commit 0f24ed8 into develop May 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant