@stonyx/cron 0.2.1-alpha.29 → 0.2.1-alpha.30
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 +0 -20
- package/dist/service.js +20 -6
- package/package.json +1 -1
package/dist/service.d.ts
CHANGED
|
@@ -119,26 +119,6 @@ export default class CronService {
|
|
|
119
119
|
* for and allows concurrent `onJobDue` invocations for the same job.
|
|
120
120
|
*/
|
|
121
121
|
executeJob(job: Job): Promise<ExecuteResult>;
|
|
122
|
-
/**
|
|
123
|
-
* Phase 1 - claim. Must be called while holding the lock (`locked()`, whose
|
|
124
|
-
* chain is module-global and therefore shared across CronService instances).
|
|
125
|
-
*
|
|
126
|
-
* Returns `null` on a successful claim, or the reason the claim was refused.
|
|
127
|
-
* "already running" is what makes a second `run()` report a skip instead of
|
|
128
|
-
* launching a concurrent invocation. "removed" covers the job being deleted
|
|
129
|
-
* between `run()`'s unlocked lookup and this lock turn - claiming then would
|
|
130
|
-
* `markRunning` an orphan and, worse, `removeFromHeap` an id that may now
|
|
131
|
-
* belong to a replacement.
|
|
132
|
-
*
|
|
133
|
-
* Detaching from the heap here (rather than relying on phase 3 to push a
|
|
134
|
-
* fresh entry) is what keeps manual runs from permanently duplicating heap
|
|
135
|
-
* entries.
|
|
136
|
-
*/
|
|
137
|
-
claimJob(job: Job): 'already running' | 'removed' | null;
|
|
138
|
-
/**
|
|
139
|
-
* Phase 3 - settle. Must be called while holding the lock.
|
|
140
|
-
*/
|
|
141
|
-
settleJob(job: Job, status: string, error: string | undefined, summary: string | undefined, startMs: number, durationMs: number): ExecuteResult;
|
|
142
122
|
removeFromHeap(id: string): void;
|
|
143
123
|
log(message: string): void;
|
|
144
124
|
}
|
package/dist/service.js
CHANGED
|
@@ -290,7 +290,7 @@ export default class CronService {
|
|
|
290
290
|
*/
|
|
291
291
|
async executeJob(job) {
|
|
292
292
|
// -- Phase 1: claim (locked) --
|
|
293
|
-
const refusal = await locked(() => this
|
|
293
|
+
const refusal = await locked(() => this.#claimJob(job));
|
|
294
294
|
if (refusal)
|
|
295
295
|
return { status: 'skipped', reason: refusal };
|
|
296
296
|
return this.#executeClaimed(job);
|
|
@@ -306,7 +306,7 @@ export default class CronService {
|
|
|
306
306
|
// Membership re-check. The claim and the invoke are no longer in the same
|
|
307
307
|
// critical section, and sibling callbacks run unlocked, so a `remove()` can
|
|
308
308
|
// land in between AND RESOLVE - it used to deadlock. A resolved `remove()`
|
|
309
|
-
// must keep meaning "this callback will not fire": settleJob's identity
|
|
309
|
+
// must keep meaning "this callback will not fire": #settleJob's identity
|
|
310
310
|
// guard only cleans up afterwards, by which point the side effect has
|
|
311
311
|
// already happened. Identity, not id, so a removed-then-replaced key is
|
|
312
312
|
// caught too. Deliberately synchronous with the `onJobDue` call below -
|
|
@@ -315,7 +315,7 @@ export default class CronService {
|
|
|
315
315
|
// This is the ONE early return in the phase-2/3 control flow that happens
|
|
316
316
|
// after a claim, so it is the one that has to release the claim by hand.
|
|
317
317
|
// (`executeJob`'s refusal return at the call site is pre-claim; `onTimer`'s
|
|
318
|
-
// re-entrancy return never claims; every return inside
|
|
318
|
+
// re-entrancy return never claims; every return inside `#settleJob` is
|
|
319
319
|
// already past phase 3.)
|
|
320
320
|
//
|
|
321
321
|
// Skipping settle entirely here is right - re-inserting or run-logging a
|
|
@@ -368,7 +368,7 @@ export default class CronService {
|
|
|
368
368
|
}
|
|
369
369
|
finally {
|
|
370
370
|
// -- Phase 3: settle (locked) --
|
|
371
|
-
settled = await locked(() => this
|
|
371
|
+
settled = await locked(() => this.#settleJob(job, status, error, summary, startMs, Date.now() - startMs));
|
|
372
372
|
}
|
|
373
373
|
return settled;
|
|
374
374
|
}
|
|
@@ -376,6 +376,15 @@ export default class CronService {
|
|
|
376
376
|
* Phase 1 - claim. Must be called while holding the lock (`locked()`, whose
|
|
377
377
|
* chain is module-global and therefore shared across CronService instances).
|
|
378
378
|
*
|
|
379
|
+
* `#private`, like `#executeClaimed` and for the same reason. Published, this
|
|
380
|
+
* was a supported call performing `markRunning` + `removeFromHeap` with no
|
|
381
|
+
* guaranteed settle - the claim-without-settle shape the try/finally in
|
|
382
|
+
* `#executeClaimed` exists to prevent, and with no lease on `runningAtMs` a
|
|
383
|
+
* single such call permanently strands the job: off the heap, marked running,
|
|
384
|
+
* with nothing that will ever release it. The precondition below cannot be
|
|
385
|
+
* expressed in the type system, so the method must not be reachable from
|
|
386
|
+
* outside the class body.
|
|
387
|
+
*
|
|
379
388
|
* Returns `null` on a successful claim, or the reason the claim was refused.
|
|
380
389
|
* "already running" is what makes a second `run()` report a skip instead of
|
|
381
390
|
* launching a concurrent invocation. "removed" covers the job being deleted
|
|
@@ -387,7 +396,7 @@ export default class CronService {
|
|
|
387
396
|
* fresh entry) is what keeps manual runs from permanently duplicating heap
|
|
388
397
|
* entries.
|
|
389
398
|
*/
|
|
390
|
-
claimJob(job) {
|
|
399
|
+
#claimJob(job) {
|
|
391
400
|
if (this.jobs.get(job.id) !== job)
|
|
392
401
|
return 'removed';
|
|
393
402
|
if (job.state.runningAtMs)
|
|
@@ -398,8 +407,13 @@ export default class CronService {
|
|
|
398
407
|
}
|
|
399
408
|
/**
|
|
400
409
|
* Phase 3 - settle. Must be called while holding the lock.
|
|
410
|
+
*
|
|
411
|
+
* `#private` for the same reason as `#claimJob`: unlocked it would run
|
|
412
|
+
* `applyResult`, a `runLog.record`, a full `removeFromHeap` rebuild, a
|
|
413
|
+
* `heap.push` and an `armTimer` with no mutual exclusion - exactly the
|
|
414
|
+
* corruption `locked()` exists to prevent.
|
|
401
415
|
*/
|
|
402
|
-
settleJob(job, status, error, summary, startMs, durationMs) {
|
|
416
|
+
#settleJob(job, status, error, summary, startMs, durationMs) {
|
|
403
417
|
try {
|
|
404
418
|
const validStatus = (status === 'ok' || status === 'error' || status === 'skipped') ? status : 'error';
|
|
405
419
|
applyResult(job, validStatus, error, durationMs);
|