Skip to content

Scheduler: replace setInterval with production-grade cron (BullMQ repeatable jobs) #1

@parag

Description

@parag

Problem

The routine scheduler uses setInterval(tick, 60_000) — a plain JS timer inside the Node.js process. This works for single-instance dev but has problems in production:

  • No persistence — if the server restarts, missed ticks are lost
  • No distributed locking — running 2+ instances fires routines twice
  • ±60s precision — checks every minute, not at exact cron time
  • No catch-up — if the server was down during a scheduled tick, it's silently skipped

Current implementation

packages/@boringos/core/src/scheduler.ts — createRoutineScheduler()
  → setInterval(tick, 60_000)
  → tick() queries active routines, matches cron, fires

Proposed solution

Use BullMQ repeatable jobs when Redis is available:

  1. When app.queue(createBullMQQueue({ redis })) is configured, the scheduler should register routines as BullMQ repeatable jobs instead of using setInterval
  2. BullMQ handles: cron precision, persistence across restarts, distributed locking (only one instance processes each job), automatic catch-up for missed jobs
  3. Fall back to current setInterval when no Redis is configured (dev mode)

Architecture

No Redis (default):
  setInterval(60s) → tick() → match cron → fire  (current behavior, unchanged)

With Redis:
  For each active routine → BullMQ.add(routineId, {}, { repeat: { cron: expression } })
  BullMQ processes → fires routine (workflow or agent wake)
  On routine CRUD → update/remove BullMQ repeatable job

Impact

  • packages/@boringos/core/src/scheduler.ts
  • packages/@boringos/pipeline (may need repeatable job support)
  • packages/@boringos/core/src/boringos.ts (pass queue adapter to scheduler)

Files

  • scheduler.ts:16createRoutineScheduler()
  • scheduler.ts:49interval = setInterval(() => tick().catch(() => {}), 60_000)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions