feat(cron): add default stagger controls for scheduled jobs

This commit is contained in:
Peter Steinberger
2026-02-17 23:46:05 +01:00
parent b98b113b88
commit c26cf6aa83
20 changed files with 907 additions and 56 deletions

View File

@@ -1,13 +1,14 @@
import crypto from "node:crypto";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { CronJob, CronJobState } from "./types.js";
import * as schedule from "./schedule.js";
import { CronService } from "./service.js";
import { computeJobNextRunAtMs } from "./service/jobs.js";
import { createCronServiceState, type CronEvent } from "./service/state.js";
import { onTimer } from "./service/timer.js";
import type { CronJob, CronJobState } from "./types.js";
const noopLogger = {
info: vi.fn(),
@@ -16,6 +17,12 @@ const noopLogger = {
debug: vi.fn(),
trace: vi.fn(),
};
const TOP_OF_HOUR_STAGGER_MS = 5 * 60 * 1_000;
function topOfHourOffsetMs(jobId: string) {
const digest = crypto.createHash("sha256").update(jobId).digest();
return digest.readUInt32BE(0) % TOP_OF_HOUR_STAGGER_MS;
}
let fixtureRoot = "";
let fixtureCount = 0;
@@ -101,13 +108,14 @@ describe("Cron issue regressions", () => {
wakeMode: "next-heartbeat",
payload: { kind: "systemEvent", text: "tick" },
});
expect(created.state.nextRunAtMs).toBe(Date.parse("2026-02-06T11:00:00.000Z"));
const offsetMs = topOfHourOffsetMs(created.id);
expect(created.state.nextRunAtMs).toBe(Date.parse("2026-02-06T11:00:00.000Z") + offsetMs);
const updated = await cron.update(created.id, {
schedule: { kind: "cron", expr: "0 */2 * * *", tz: "UTC" },
});
expect(updated.state.nextRunAtMs).toBe(Date.parse("2026-02-06T12:00:00.000Z"));
expect(updated.state.nextRunAtMs).toBe(Date.parse("2026-02-06T12:00:00.000Z") + offsetMs);
const forceNow = await cron.add({
name: "force-now",