jm2 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/daemon/scheduler.js +72 -3
package/package.json
CHANGED
package/src/daemon/scheduler.js
CHANGED
|
@@ -133,7 +133,21 @@ export class Scheduler {
|
|
|
133
133
|
// Calculate next run time for active jobs
|
|
134
134
|
let nextRun = null;
|
|
135
135
|
if (job.status === JobStatus.ACTIVE) {
|
|
136
|
-
nextRun
|
|
136
|
+
// Check if there's a stored nextRun that is in the past (missed execution)
|
|
137
|
+
const storedNextRun = job.nextRun || job.nextRunISO;
|
|
138
|
+
if (storedNextRun) {
|
|
139
|
+
const storedDate = new Date(storedNextRun);
|
|
140
|
+
const now = new Date();
|
|
141
|
+
// If stored nextRun is in the past and it's a cron job, keep it so tick() will detect it as overdue
|
|
142
|
+
if (storedDate < now && job.type === JobType.CRON) {
|
|
143
|
+
nextRun = storedDate;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// If nextRun wasn't set from stored value, calculate a new one
|
|
148
|
+
if (!nextRun) {
|
|
149
|
+
nextRun = this.calculateNextRun(job, new Date());
|
|
150
|
+
}
|
|
137
151
|
}
|
|
138
152
|
|
|
139
153
|
this.jobs.set(job.id, {
|
|
@@ -193,6 +207,34 @@ export class Scheduler {
|
|
|
193
207
|
return nextRun;
|
|
194
208
|
}
|
|
195
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Find periodic jobs that are overdue (nextRun is in the past)
|
|
212
|
+
* This handles system sleep/wake scenarios where jobs should have run while asleep
|
|
213
|
+
* @param {Date} now - Current time
|
|
214
|
+
* @returns {Array} Array of overdue job IDs
|
|
215
|
+
*/
|
|
216
|
+
findOverduePeriodicJobs(now) {
|
|
217
|
+
const overdueJobs = [];
|
|
218
|
+
|
|
219
|
+
for (const [id, job] of this.jobs) {
|
|
220
|
+
if (
|
|
221
|
+
job.status === JobStatus.ACTIVE &&
|
|
222
|
+
job.type === JobType.CRON &&
|
|
223
|
+
job.cron &&
|
|
224
|
+
job.nextRun
|
|
225
|
+
) {
|
|
226
|
+
const timeSinceNextRun = now.getTime() - job.nextRun.getTime();
|
|
227
|
+
const isOverdue = timeSinceNextRun > this.checkIntervalMs * 2;
|
|
228
|
+
|
|
229
|
+
if (isOverdue) {
|
|
230
|
+
overdueJobs.push(id);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return overdueJobs;
|
|
236
|
+
}
|
|
237
|
+
|
|
196
238
|
/**
|
|
197
239
|
* Recalculate next run times for periodic jobs that have drifted into the past
|
|
198
240
|
* This handles system sleep/wake scenarios where nextRun becomes stale
|
|
@@ -321,8 +363,27 @@ export class Scheduler {
|
|
|
321
363
|
|
|
322
364
|
const nowDate = new Date();
|
|
323
365
|
|
|
324
|
-
//
|
|
325
|
-
// This
|
|
366
|
+
// Check for overdue jobs BEFORE recalculating next run times
|
|
367
|
+
// This ensures jobs that should have run while system was asleep get executed
|
|
368
|
+
const overdueJobIds = this.findOverduePeriodicJobs(nowDate);
|
|
369
|
+
if (overdueJobIds.length > 0) {
|
|
370
|
+
this.logger.info(`Found ${overdueJobIds.length} overdue job(s) to execute after system wake`);
|
|
371
|
+
for (const jobId of overdueJobIds) {
|
|
372
|
+
const job = this.jobs.get(jobId);
|
|
373
|
+
if (job) {
|
|
374
|
+
this.logger.info(`Executing overdue job ${jobId} (${job.name || 'unnamed'})`);
|
|
375
|
+
// Execute immediately without waiting for the normal due jobs check
|
|
376
|
+
this.executeJob(job);
|
|
377
|
+
// Calculate next run from the original scheduled time to maintain cadence
|
|
378
|
+
const originalNextRun = job.nextRun ? new Date(job.nextRun) : new Date();
|
|
379
|
+
const nextRun = this.calculateNextRunAfterExecution(job, originalNextRun);
|
|
380
|
+
this.updateJobNextRun(jobId, nextRun);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Recalculate next run for any remaining periodic jobs that have drifted into the past
|
|
386
|
+
// This handles edge cases and ensures nextRun times are in the future
|
|
326
387
|
this.recalculateStalePeriodicJobs(nowDate);
|
|
327
388
|
|
|
328
389
|
const dueJobs = this.getDueJobs();
|
|
@@ -535,6 +596,14 @@ export class Scheduler {
|
|
|
535
596
|
...job,
|
|
536
597
|
nextRun: job.nextRun ? job.nextRun.toISOString() : null,
|
|
537
598
|
}));
|
|
599
|
+
|
|
600
|
+
// Safety check: don't persist empty jobs array if we had jobs before
|
|
601
|
+
// This prevents accidental data loss during startup/shutdown
|
|
602
|
+
if (jobsArray.length === 0 && this.jobs.size > 0) {
|
|
603
|
+
this.logger.error('BUG: Attempted to save empty jobs array but jobs Map is not empty!');
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
|
|
538
607
|
saveJobs(jobsArray);
|
|
539
608
|
}
|
|
540
609
|
|