jm2 0.1.10 → 0.1.13

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jm2",
3
- "version": "0.1.10",
3
+ "version": "0.1.13",
4
4
  "description": "Job Manager 2 - A simple yet powerful job scheduler combining cron and at functionality",
5
5
  "type": "module",
6
6
  "main": "src/cli/index.js",
@@ -178,8 +178,6 @@ class DarwinService extends PlatformService {
178
178
  </array>
179
179
  <key>RunAtLoad</key>
180
180
  <true/>
181
- <key>KeepAlive</key>
182
- <true/>
183
181
  <key>StandardOutPath</key>
184
182
  <string>${stdoutPath}</string>
185
183
  <key>StandardErrorPath</key>
@@ -344,8 +342,6 @@ Type=forking
344
342
  ExecStart=${nodePath} ${jm2Path} start
345
343
  ExecStop=${nodePath} ${jm2Path} stop
346
344
  ExecReload=${nodePath} ${jm2Path} restart
347
- Restart=always
348
- RestartSec=10
349
345
  Environment="JM2_DATA_DIR=${dataDir}"
350
346
  StandardOutput=append:${join(logDir, 'service-out.log')}
351
347
  StandardError=append:${join(logDir, 'service-err.log')}
@@ -193,9 +193,38 @@ export class Scheduler {
193
193
  return nextRun;
194
194
  }
195
195
 
196
+ /**
197
+ * Find periodic jobs that are overdue (nextRun is in the past)
198
+ * This handles system sleep/wake scenarios where jobs should have run while asleep
199
+ * @param {Date} now - Current time
200
+ * @returns {Array} Array of overdue job IDs
201
+ */
202
+ findOverduePeriodicJobs(now) {
203
+ const overdueJobs = [];
204
+
205
+ for (const [id, job] of this.jobs) {
206
+ if (
207
+ job.status === JobStatus.ACTIVE &&
208
+ job.type === JobType.CRON &&
209
+ job.cron &&
210
+ job.nextRun
211
+ ) {
212
+ const timeSinceNextRun = now.getTime() - job.nextRun.getTime();
213
+ const isOverdue = timeSinceNextRun > this.checkIntervalMs * 2;
214
+
215
+ if (isOverdue) {
216
+ overdueJobs.push(id);
217
+ }
218
+ }
219
+ }
220
+
221
+ return overdueJobs;
222
+ }
223
+
196
224
  /**
197
225
  * Recalculate next run times for periodic jobs that have drifted into the past
198
226
  * This handles system sleep/wake scenarios where nextRun becomes stale
227
+ * Only recalculates jobs that are significantly overdue (missed multiple runs)
199
228
  * @param {Date} now - Current time
200
229
  */
201
230
  recalculateStalePeriodicJobs(now) {
@@ -204,17 +233,21 @@ export class Scheduler {
204
233
  job.status === JobStatus.ACTIVE &&
205
234
  job.type === JobType.CRON &&
206
235
  job.cron &&
207
- job.nextRun &&
208
- job.nextRun < now
236
+ job.nextRun
209
237
  ) {
210
- // Job's next run is in the past - recalculate from now to find next future occurrence
211
- const newNextRun = this.calculateNextRun(job, now);
212
- if (newNextRun && newNextRun !== job.nextRun) {
213
- this.logger.debug(
214
- `Recalculating next run for job ${id} (${job.name || 'unnamed'}): ` +
215
- `${job.nextRun.toISOString()} ${newNextRun.toISOString()}`
216
- );
217
- this.updateJobNextRun(id, newNextRun);
238
+ const timeSinceNextRun = now.getTime() - job.nextRun.getTime();
239
+ const isSignificantlyOverdue = timeSinceNextRun > this.checkIntervalMs * 2;
240
+
241
+ if (isSignificantlyOverdue) {
242
+ // Job is significantly overdue - recalculate from now to find next future occurrence
243
+ const newNextRun = this.calculateNextRun(job, now);
244
+ if (newNextRun && newNextRun !== job.nextRun) {
245
+ this.logger.debug(
246
+ `Recalculating next run for job ${id} (${job.name || 'unnamed'}): ` +
247
+ `${job.nextRun.toISOString()} → ${newNextRun.toISOString()}`
248
+ );
249
+ this.updateJobNextRun(id, newNextRun);
250
+ }
218
251
  }
219
252
  }
220
253
  }
@@ -316,8 +349,26 @@ export class Scheduler {
316
349
 
317
350
  const nowDate = new Date();
318
351
 
319
- // Recalculate next run for periodic jobs that have drifted into the past
320
- // This handles system sleep/wake scenarios
352
+ // Check for overdue jobs BEFORE recalculating next run times
353
+ // This ensures jobs that should have run while system was asleep get executed
354
+ const overdueJobIds = this.findOverduePeriodicJobs(nowDate);
355
+ if (overdueJobIds.length > 0) {
356
+ this.logger.info(`Found ${overdueJobIds.length} overdue job(s) to execute after system wake`);
357
+ for (const jobId of overdueJobIds) {
358
+ const job = this.jobs.get(jobId);
359
+ if (job) {
360
+ this.logger.info(`Executing overdue job ${jobId} (${job.name || 'unnamed'})`);
361
+ // Execute immediately without waiting for the normal due jobs check
362
+ this.executeJob(job);
363
+ // Update next run time from now for the next scheduled occurrence
364
+ const nextRun = this.calculateNextRun(job, nowDate);
365
+ this.updateJobNextRun(jobId, nextRun);
366
+ }
367
+ }
368
+ }
369
+
370
+ // Recalculate next run for any remaining periodic jobs that have drifted into the past
371
+ // This handles edge cases and ensures nextRun times are in the future
321
372
  this.recalculateStalePeriodicJobs(nowDate);
322
373
 
323
374
  const dueJobs = this.getDueJobs();