@stonyx/cron 0.2.1-alpha.16 → 0.2.1-alpha.17

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.
Files changed (2) hide show
  1. package/dist/service.js +47 -34
  2. package/package.json +1 -1
package/dist/service.js CHANGED
@@ -344,45 +344,58 @@ export default class CronService {
344
344
  * Phase 3 - settle. Must be called while holding the lock.
345
345
  */
346
346
  settleJob(job, status, error, summary, startMs, durationMs) {
347
- const validStatus = (status === 'ok' || status === 'error' || status === 'skipped') ? status : 'error';
348
- applyResult(job, validStatus, error, durationMs);
349
- // The callback ran unlocked, so it may have removed this job while it was
350
- // in flight. Do not resurrect a removed job's heap entry or run log.
351
- if (this.jobs.get(job.id) !== job) {
347
+ try {
348
+ const validStatus = (status === 'ok' || status === 'error' || status === 'skipped') ? status : 'error';
349
+ applyResult(job, validStatus, error, durationMs);
350
+ // The callback ran unlocked, so this job may have been removed - or
351
+ // removed and re-registered under the same id (the shape
352
+ // `start(initialJobs)` uses) - while it was in flight. Identity, not id.
353
+ //
354
+ // Deliberately touch NOTHING here. The claim phase already detached this
355
+ // job's own heap entry and nothing re-added it, so there is nothing to
356
+ // clean up; any entry now filed under this id belongs to the
357
+ // replacement, and removing it by id would silently unschedule a live
358
+ // job. Do not resurrect a removed job's heap entry or run log either.
359
+ if (this.jobs.get(job.id) !== job) {
360
+ return { status, error, summary, durationMs };
361
+ }
362
+ // Log the run
363
+ this.runLog.record({
364
+ jobId: job.id,
365
+ status,
366
+ error,
367
+ summary,
368
+ runAtMs: startMs,
369
+ durationMs,
370
+ nextRunAtMs: job.state.nextRunAtMs,
371
+ });
372
+ // Handle one-shot auto-delete. The callback ran unlocked and may have
373
+ // pushed a heap entry for this job via update(), so drop it - the job is
374
+ // about to stop existing.
375
+ if (job.deleteAfterRun && status === 'ok' && !job.enabled) {
376
+ this.jobs.delete(job.id);
377
+ this.removeFromHeap(job.id);
378
+ this.runLog.removeJob(job.id);
379
+ return { status, summary, deleted: true };
380
+ }
381
+ // Re-insert into heap if still active. The callback ran unlocked, so it
382
+ // may itself have added a heap entry for this job (via add/update); drop
383
+ // any such entry first to preserve one-entry-per-key.
352
384
  this.removeFromHeap(job.id);
353
- this.armTimer();
385
+ if (job.enabled && job.state.nextRunAtMs) {
386
+ this.heap.push({ key: job.id, nextTrigger: job.state.nextRunAtMs });
387
+ }
354
388
  return { status, error, summary, durationMs };
355
389
  }
356
- // Log the run
357
- this.runLog.record({
358
- jobId: job.id,
359
- status,
360
- error,
361
- summary,
362
- runAtMs: startMs,
363
- durationMs,
364
- nextRunAtMs: job.state.nextRunAtMs,
365
- });
366
- // Handle one-shot auto-delete
367
- if (job.deleteAfterRun && status === 'ok' && !job.enabled) {
368
- this.jobs.delete(job.id);
369
- this.removeFromHeap(job.id);
370
- this.runLog.removeJob(job.id);
390
+ finally {
391
+ // One re-arm covering every exit, rather than one per branch. The claim
392
+ // phase detached this job from the heap, so a timer that fired during the
393
+ // unlocked invoke would have found an empty heap and armed nothing -
394
+ // and `run()` has no `finally { armTimer() }` of its own the way
395
+ // `onTimer` does. Without this a manual run() can leave the scheduler
396
+ // with no pending wake at all.
371
397
  this.armTimer();
372
- return { status, summary, deleted: true };
373
398
  }
374
- // Re-insert into heap if still active. The callback ran unlocked, so it
375
- // may itself have added a heap entry for this job (via add/update); drop
376
- // any such entry first to preserve one-entry-per-key.
377
- this.removeFromHeap(job.id);
378
- if (job.enabled && job.state.nextRunAtMs) {
379
- this.heap.push({ key: job.id, nextTrigger: job.state.nextRunAtMs });
380
- }
381
- // The claim phase detached this job from the heap, so a timer that fired
382
- // during the unlocked invoke would have seen it missing. Re-arm here so a
383
- // manual run() can never leave the scheduler without a pending wake.
384
- this.armTimer();
385
- return { status, error, summary, durationMs };
386
399
  }
387
400
  // -- Helpers ---------------------------------------------------------
388
401
  removeFromHeap(id) {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "keywords": [
4
4
  "stonyx-module"
5
5
  ],
6
- "version": "0.2.1-alpha.16",
6
+ "version": "0.2.1-alpha.17",
7
7
  "description": "Cron/job scheduler for Stonyx framework",
8
8
  "main": "dist/main.js",
9
9
  "types": "dist/main.d.ts",