@stonyx/cron 0.2.1-alpha.14 → 0.2.1-alpha.16
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/dist/service.d.ts +13 -6
- package/dist/service.js +27 -9
- package/package.json +1 -1
package/dist/service.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ interface ExecuteResult {
|
|
|
15
15
|
summary?: string;
|
|
16
16
|
durationMs?: number;
|
|
17
17
|
deleted?: boolean;
|
|
18
|
-
|
|
18
|
+
/** Only set when `status` is `'skipped'`. */
|
|
19
|
+
reason?: 'not due' | 'already running' | 'removed';
|
|
19
20
|
}
|
|
20
21
|
interface ServiceStatus {
|
|
21
22
|
started: boolean;
|
|
@@ -97,12 +98,18 @@ export default class CronService {
|
|
|
97
98
|
/**
|
|
98
99
|
* Phase 1 - claim. Must be called while holding the lock.
|
|
99
100
|
*
|
|
100
|
-
* Returns
|
|
101
|
-
* "already running"
|
|
102
|
-
*
|
|
103
|
-
*
|
|
101
|
+
* Returns `null` on a successful claim, or the reason the claim was refused.
|
|
102
|
+
* "already running" is what makes a second `run()` report a skip instead of
|
|
103
|
+
* launching a concurrent invocation. "removed" covers the job being deleted
|
|
104
|
+
* between `run()`'s unlocked lookup and this lock turn - claiming then would
|
|
105
|
+
* `markRunning` an orphan and, worse, `removeFromHeap` an id that may now
|
|
106
|
+
* belong to a replacement.
|
|
107
|
+
*
|
|
108
|
+
* Detaching from the heap here (rather than relying on phase 3 to push a
|
|
109
|
+
* fresh entry) is what keeps manual runs from permanently duplicating heap
|
|
110
|
+
* entries.
|
|
104
111
|
*/
|
|
105
|
-
claimJob(job: Job):
|
|
112
|
+
claimJob(job: Job): 'already running' | 'removed' | null;
|
|
106
113
|
/**
|
|
107
114
|
* Phase 3 - settle. Must be called while holding the lock.
|
|
108
115
|
*/
|
package/dist/service.js
CHANGED
|
@@ -268,10 +268,20 @@ export default class CronService {
|
|
|
268
268
|
async executeJob(job, alreadyClaimed = false) {
|
|
269
269
|
// -- Phase 1: claim (locked) --
|
|
270
270
|
if (!alreadyClaimed) {
|
|
271
|
-
const
|
|
272
|
-
if (
|
|
273
|
-
return { status: 'skipped', reason:
|
|
271
|
+
const refusal = await locked(() => this.claimJob(job));
|
|
272
|
+
if (refusal)
|
|
273
|
+
return { status: 'skipped', reason: refusal };
|
|
274
274
|
}
|
|
275
|
+
// Membership re-check. The claim and the invoke are no longer in the same
|
|
276
|
+
// critical section, and sibling callbacks run unlocked, so a `remove()` can
|
|
277
|
+
// land in between AND RESOLVE - it used to deadlock. A resolved `remove()`
|
|
278
|
+
// must keep meaning "this callback will not fire": settleJob's identity
|
|
279
|
+
// guard only cleans up afterwards, by which point the side effect has
|
|
280
|
+
// already happened. Identity, not id, so a removed-then-replaced key is
|
|
281
|
+
// caught too. Deliberately synchronous with the `onJobDue` call below -
|
|
282
|
+
// nothing can interleave between this check and the invocation.
|
|
283
|
+
if (this.jobs.get(job.id) !== job)
|
|
284
|
+
return { status: 'skipped', reason: 'removed' };
|
|
275
285
|
const startMs = Date.now();
|
|
276
286
|
let status = 'ok';
|
|
277
287
|
let error;
|
|
@@ -310,17 +320,25 @@ export default class CronService {
|
|
|
310
320
|
/**
|
|
311
321
|
* Phase 1 - claim. Must be called while holding the lock.
|
|
312
322
|
*
|
|
313
|
-
* Returns
|
|
314
|
-
* "already running"
|
|
315
|
-
*
|
|
316
|
-
*
|
|
323
|
+
* Returns `null` on a successful claim, or the reason the claim was refused.
|
|
324
|
+
* "already running" is what makes a second `run()` report a skip instead of
|
|
325
|
+
* launching a concurrent invocation. "removed" covers the job being deleted
|
|
326
|
+
* between `run()`'s unlocked lookup and this lock turn - claiming then would
|
|
327
|
+
* `markRunning` an orphan and, worse, `removeFromHeap` an id that may now
|
|
328
|
+
* belong to a replacement.
|
|
329
|
+
*
|
|
330
|
+
* Detaching from the heap here (rather than relying on phase 3 to push a
|
|
331
|
+
* fresh entry) is what keeps manual runs from permanently duplicating heap
|
|
332
|
+
* entries.
|
|
317
333
|
*/
|
|
318
334
|
claimJob(job) {
|
|
335
|
+
if (this.jobs.get(job.id) !== job)
|
|
336
|
+
return 'removed';
|
|
319
337
|
if (job.state.runningAtMs)
|
|
320
|
-
return
|
|
338
|
+
return 'already running';
|
|
321
339
|
markRunning(job);
|
|
322
340
|
this.removeFromHeap(job.id);
|
|
323
|
-
return
|
|
341
|
+
return null;
|
|
324
342
|
}
|
|
325
343
|
/**
|
|
326
344
|
* Phase 3 - settle. Must be called while holding the lock.
|