Skip to content

Commit 2bdf413

Browse files
authored
Merge pull request #369 from Hexagon/copilot/investigate-ci-test-failures
Fix large-negative setTimeout delay causing allowPast jobs to never fire in Deno
2 parents 849b0d0 + b45c147 commit 2bdf413

File tree

2 files changed

+44
-0
lines changed

2 files changed

+44
-0
lines changed

src/croner.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,13 @@ class Cron<T = undefined> {
499499
waitMs = maxDelay;
500500
}
501501

502+
// Clamp negative delays to 0 - some runtimes (e.g. Deno) treat large negative values
503+
// as large positive delays due to 32-bit integer overflow, causing jobs with allowPast:true
504+
// and a far-past date to never fire. Use a backoff when paused to avoid a tight loop.
505+
if (waitMs < 0) {
506+
waitMs = this._states.paused ? 1000 : 0;
507+
}
508+
502509
// Start the timer loop
503510
// _checkTrigger will either call _trigger (if it's time, croner isn't paused and whatever),
504511
// or recurse back to this function to wait for next trigger

test/croner.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,43 @@ test(
11591159
}),
11601160
);
11611161

1162+
test(
1163+
"Fire-once job with allowPast: true, paused: true, and far-past date should fire after resume",
1164+
//@ts-ignore
1165+
timeout(4000, (resolve) => {
1166+
let fired = false;
1167+
let resumed = false;
1168+
let settled = false;
1169+
let watchdog: ReturnType<typeof setTimeout> | undefined;
1170+
const veryOldDate = new Date("2020-01-01T00:00:00");
1171+
const job = new Cron(veryOldDate, { allowPast: true, paused: true }, () => {
1172+
fired = true;
1173+
1174+
if (!resumed || settled) return;
1175+
1176+
settled = true;
1177+
if (watchdog) clearTimeout(watchdog);
1178+
job.stop();
1179+
assertEquals(fired, true);
1180+
resolve();
1181+
});
1182+
1183+
// Should not fire while paused
1184+
setTimeout(() => {
1185+
assertEquals(fired, false);
1186+
resumed = true;
1187+
job.resume();
1188+
1189+
watchdog = setTimeout(() => {
1190+
if (settled) return;
1191+
settled = true;
1192+
job.stop();
1193+
assertEquals(fired, true);
1194+
}, 2000);
1195+
}, 100);
1196+
}),
1197+
);
1198+
11621199
test("Fire-once job with no allowPast and date > 1s in past should have null nextRun and not be running", function () {
11631200
// A once-job 3 seconds in the past without allowPast should silently not schedule
11641201
const pastTime = new Date(Date.now() - 3000);

0 commit comments

Comments
 (0)