mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 12:07:40 +00:00
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 1ffe6a45af
Co-authored-by: pierreeurope <248892285+pierreeurope@users.noreply.github.com>
Co-authored-by: sebslight <19554889+sebslight@users.noreply.github.com>
Reviewed-by: @sebslight
This commit is contained in:
@@ -102,6 +102,35 @@ export function computeJobNextRunAtMs(job: CronJob, nowMs: number): number | und
|
||||
/** Maximum consecutive schedule errors before auto-disabling a job. */
|
||||
const MAX_SCHEDULE_ERRORS = 3;
|
||||
|
||||
function recordScheduleComputeError(params: {
|
||||
state: CronServiceState;
|
||||
job: CronJob;
|
||||
err: unknown;
|
||||
}): boolean {
|
||||
const { state, job, err } = params;
|
||||
const errorCount = (job.state.scheduleErrorCount ?? 0) + 1;
|
||||
const errText = String(err);
|
||||
|
||||
job.state.scheduleErrorCount = errorCount;
|
||||
job.state.nextRunAtMs = undefined;
|
||||
job.state.lastError = `schedule error: ${errText}`;
|
||||
|
||||
if (errorCount >= MAX_SCHEDULE_ERRORS) {
|
||||
job.enabled = false;
|
||||
state.deps.log.error(
|
||||
{ jobId: job.id, name: job.name, errorCount, err: errText },
|
||||
"cron: auto-disabled job after repeated schedule errors",
|
||||
);
|
||||
} else {
|
||||
state.deps.log.warn(
|
||||
{ jobId: job.id, name: job.name, errorCount, err: errText },
|
||||
"cron: failed to compute next run for job (skipping)",
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function normalizeJobTickState(params: { state: CronServiceState; job: CronJob; nowMs: number }): {
|
||||
changed: boolean;
|
||||
skip: boolean;
|
||||
@@ -184,23 +213,8 @@ export function recomputeNextRuns(state: CronServiceState): boolean {
|
||||
changed = true;
|
||||
}
|
||||
} catch (err) {
|
||||
const errorCount = (job.state.scheduleErrorCount ?? 0) + 1;
|
||||
job.state.scheduleErrorCount = errorCount;
|
||||
job.state.nextRunAtMs = undefined;
|
||||
job.state.lastError = `schedule error: ${String(err)}`;
|
||||
changed = true;
|
||||
|
||||
if (errorCount >= MAX_SCHEDULE_ERRORS) {
|
||||
job.enabled = false;
|
||||
state.deps.log.error(
|
||||
{ jobId: job.id, name: job.name, errorCount, err: String(err) },
|
||||
"cron: auto-disabled job after repeated schedule errors",
|
||||
);
|
||||
} else {
|
||||
state.deps.log.warn(
|
||||
{ jobId: job.id, name: job.name, errorCount, err: String(err) },
|
||||
"cron: failed to compute next run for job (skipping)",
|
||||
);
|
||||
if (recordScheduleComputeError({ state, job, err })) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,10 +236,21 @@ export function recomputeNextRunsForMaintenance(state: CronServiceState): boolea
|
||||
// If a job was past-due but not found by findDueJobs, recomputing would
|
||||
// cause it to be silently skipped.
|
||||
if (job.state.nextRunAtMs === undefined) {
|
||||
const newNext = computeJobNextRunAtMs(job, now);
|
||||
if (newNext !== undefined) {
|
||||
job.state.nextRunAtMs = newNext;
|
||||
changed = true;
|
||||
try {
|
||||
const newNext = computeJobNextRunAtMs(job, now);
|
||||
if (job.state.nextRunAtMs !== newNext) {
|
||||
job.state.nextRunAtMs = newNext;
|
||||
changed = true;
|
||||
}
|
||||
// Clear schedule error count on successful computation.
|
||||
if (job.state.scheduleErrorCount) {
|
||||
job.state.scheduleErrorCount = undefined;
|
||||
changed = true;
|
||||
}
|
||||
} catch (err) {
|
||||
if (recordScheduleComputeError({ state, job, err })) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
|
||||
@@ -7,7 +7,6 @@ import { sweepCronRunSessions } from "../session-reaper.js";
|
||||
import {
|
||||
computeJobNextRunAtMs,
|
||||
nextWakeAtMs,
|
||||
recomputeNextRuns,
|
||||
recomputeNextRunsForMaintenance,
|
||||
resolveJobPayloadTextForMain,
|
||||
} from "./jobs.js";
|
||||
@@ -283,7 +282,12 @@ export async function onTimer(state: CronServiceState) {
|
||||
}
|
||||
}
|
||||
|
||||
recomputeNextRuns(state);
|
||||
// Use maintenance-only recompute to avoid advancing past-due
|
||||
// nextRunAtMs values that became due between findDueJobs and this
|
||||
// locked block. The full recomputeNextRuns would silently skip
|
||||
// those jobs (advancing nextRunAtMs without execution), causing
|
||||
// daily cron schedules to jump 48 h instead of 24 h (#17852).
|
||||
recomputeNextRunsForMaintenance(state);
|
||||
await persist(state);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user