Skip to content

fix(cron): bound tick cost and make every accepted schedule fire - #19

Merged
TheGreatAxios merged 1 commit into
cl-9391-cron-align-repo-scaffolding-with-the-package-standardfrom
cl-9447-cron-bound-tick-cost-and-make-every-accepted-schedule-fire
Sep 27, 2026
Merged

TheGreatAxios merged 1 commit into
cl-9391-cron-align-repo-scaffolding-with-the-package-standardfrom
cl-9447-cron-bound-tick-cost-and-make-every-accepted-schedule-fire

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

Summary

  • Each expression is parsed once into allowed-value sets (parseCronExpression), capped at 256 characters and 64 comma clauses. A 5,000-clause expression is rejected at parse time instead of costing minutes of CPU per tick.
  • The next-fire search skips non-matching days and hours, so it costs a few steps per day. The lookahead is eight years plus a day, which covers Feb 29 across 2100.
  • The ticker asks whether any minute after lastFiredAt (or createdAt) and at or before now matches, and only searches the last lookahead window. A Feb 29 schedule now fires, and an old anchor costs no more.
  • POST / rejects an expression that can never fire (cronExpressionCanFire, now arithmetic). The ticker stops such a row with stoppedReason: "invalid_expression" and calls onScheduleStopped, instead of re-scanning it.
  • n/step without a range end steps to the field maximum (5/2 → 5,7,…,59).
  • DOM/DOW Vixie OR semantics are unchanged.

Stacked on #18.

Verification

bun run check, build, test:e2e against local Postgres, and the Node pack smoke pass. The new unit and e2e tests fail on #18 first. They cover 5/2, the caps, never-firing expressions, Feb 29 (unit and e2e), OR semantics, and timing bounds.

Closes CL-9447

Each expression is parsed once into allowed-value sets, capped at 256
characters and 64 clauses. The next-fire search skips non-matching days
and hours, so it costs a few steps per day and covers eight years, long
enough for Feb 29 across 2100. The ticker asks whether any minute after
lastFiredAt (or createdAt) and at or before now matches, searching only
the last window, so a Feb 29 schedule fires and an old anchor costs no
more. POST rejects an expression that can never fire; the ticker stops
such a row with invalid_expression instead of re-scanning it. `5/2` now
steps to the field maximum. DOM/DOW OR semantics are unchanged.

Closes CL-9447
@TheGreatAxios
TheGreatAxios added this pull request to stack #16 September 27, 2026 02:27
@TheGreatAxios
TheGreatAxios merged commit 7d0113c into main Sep 27, 2026
1 check 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