fix(cron): preserve reload skip across repeated force reloads
This commit is contained in:
parent
ce43e39dcb
commit
a42cd9473c
@ -247,4 +247,59 @@ describe("forceReload repairs externally changed cron schedules", () => {
|
||||
recomputeNextRunsForMaintenance(state);
|
||||
expect(state.store?.jobs[0]?.state.scheduleErrorCount).toBe(2);
|
||||
});
|
||||
|
||||
it("preserves the one-shot skip across a second forceReload before maintenance recompute", async () => {
|
||||
const store = await makeStorePath();
|
||||
const nowMs = Date.parse("2026-03-19T01:44:00.000Z");
|
||||
const jobId = "external-invalid-schedule-second-reload";
|
||||
|
||||
const createJob = (expr: string): CronJob => ({
|
||||
id: jobId,
|
||||
name: "external invalid schedule second reload",
|
||||
enabled: true,
|
||||
createdAtMs: Date.parse("2026-03-18T00:30:00.000Z"),
|
||||
updatedAtMs: Date.parse("2026-03-19T01:44:00.000Z"),
|
||||
schedule: { kind: "cron", expr, tz: "Asia/Shanghai", staggerMs: 0 },
|
||||
sessionTarget: "main",
|
||||
wakeMode: "next-heartbeat",
|
||||
payload: { kind: "systemEvent", text: "tick" },
|
||||
state: {
|
||||
nextRunAtMs: Date.parse("2026-03-20T00:30:00.000Z"),
|
||||
},
|
||||
});
|
||||
|
||||
await writeCronStoreSnapshot({
|
||||
storePath: store.storePath,
|
||||
jobs: [createJob("30 8 * * *")],
|
||||
});
|
||||
|
||||
const state = createCronServiceState({
|
||||
cronEnabled: true,
|
||||
storePath: store.storePath,
|
||||
log: noopLogger,
|
||||
nowMs: () => nowMs,
|
||||
enqueueSystemEvent: vi.fn(),
|
||||
requestHeartbeatNow: vi.fn(),
|
||||
runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" as const })),
|
||||
});
|
||||
|
||||
await ensureLoaded(state, { skipRecompute: true });
|
||||
|
||||
await writeCronStoreSnapshot({
|
||||
storePath: store.storePath,
|
||||
jobs: [createJob("not a valid cron")],
|
||||
});
|
||||
|
||||
await ensureLoaded(state, { forceReload: true, skipRecompute: true });
|
||||
expect(state.store?.jobs[0]?.state.scheduleErrorCount).toBe(1);
|
||||
|
||||
await ensureLoaded(state, { forceReload: true, skipRecompute: true });
|
||||
expect(state.store?.jobs[0]?.state.scheduleErrorCount).toBe(1);
|
||||
|
||||
recomputeNextRunsForMaintenance(state);
|
||||
expect(state.store?.jobs[0]?.state.scheduleErrorCount).toBe(1);
|
||||
|
||||
recomputeNextRunsForMaintenance(state);
|
||||
expect(state.store?.jobs[0]?.state.scheduleErrorCount).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
@ -40,10 +40,17 @@ function repairNextRunsAfterExternalReload(params: {
|
||||
}): boolean {
|
||||
const { state, previousJobs } = params;
|
||||
const skipRecomputeJobIds = getSkipNextReloadRepairRecomputeJobIds(state);
|
||||
skipRecomputeJobIds.clear();
|
||||
if (!state.store || !previousJobs?.length) {
|
||||
return false;
|
||||
}
|
||||
if (skipRecomputeJobIds.size > 0) {
|
||||
const currentJobIds = new Set(state.store.jobs.map((job) => job.id));
|
||||
for (const jobId of skipRecomputeJobIds) {
|
||||
if (!currentJobIds.has(jobId)) {
|
||||
skipRecomputeJobIds.delete(jobId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const previousById = new Map(previousJobs.map((job) => [job.id, job]));
|
||||
const now = state.deps.nowMs();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user