@stonyx/cron 0.2.1-alpha.55 → 0.2.1-alpha.56
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.js +51 -23
- package/package.json +1 -1
package/dist/service.js
CHANGED
|
@@ -100,32 +100,60 @@ export default class CronService {
|
|
|
100
100
|
if (this.started)
|
|
101
101
|
return;
|
|
102
102
|
this.started = true;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
103
|
+
// `finally`, not a trailing statement. Reconciled with the same guard #53
|
|
104
|
+
// puts around `register`'s `runOnInit` invocation: a scheduler that is
|
|
105
|
+
// marked started but never armed is the terminal state both fixes exist to
|
|
106
|
+
// remove, reached here through the other entry point. `initialJobs` crosses
|
|
107
|
+
// a serialization boundary — it is whatever the consumer's store handed
|
|
108
|
+
// back — so `Job[]` is a compile-time claim about runtime data, and a row
|
|
109
|
+
// missing `state` throws mid-loop. Without this, `start()` leaves
|
|
110
|
+
// `started: true` (so it is now a no-op), the rows registered before the
|
|
111
|
+
// throw sitting in the heap, and NO timer: measured, `status()` then
|
|
112
|
+
// reports `{ started: true, jobCount: 1, nextWakeAtMs: <real> }` while
|
|
113
|
+
// nothing will ever fire. Silent and healthy-looking, again.
|
|
114
|
+
try {
|
|
115
|
+
if (initialJobs) {
|
|
116
|
+
for (const job of initialJobs) {
|
|
117
|
+
// A `runningAtMs` on a rehydrated job is always stale. The claim it
|
|
118
|
+
// records was taken by a process that is gone, so nothing will ever
|
|
119
|
+
// settle it, and nothing reaps it — there is no lease on the field
|
|
120
|
+
// (tracked on #35). Left in place it is a permanently dead job that
|
|
121
|
+
// still reports healthy: `isDue` returns false forever because of the
|
|
122
|
+
// flag, `run()` answers `'already running'` forever, `update()` never
|
|
123
|
+
// touches `state.runningAtMs`, and `status()` counts it like any other.
|
|
124
|
+
// The consumer's only recovery would be remove() + add(), losing the
|
|
125
|
+
// job id and its run history.
|
|
126
|
+
//
|
|
127
|
+
// Same hazard, same treatment as the hand-release on the `'removed'`
|
|
128
|
+
// path in `#executeClaimed`: a claim with no reachable settle must be
|
|
129
|
+
// released. Assigned directly rather than via `applyResult` for the same
|
|
130
|
+
// reason — this releases the claim and nothing else. The job did not
|
|
131
|
+
// run, so it gets no run-log row, no `lastStatus`, and no recomputed
|
|
132
|
+
// `nextRunAtMs`; it is rescheduled from the store's own value below.
|
|
133
|
+
//
|
|
134
|
+
// Written unconditionally, and deliberately NOT guarded on a
|
|
135
|
+
// `job.state.runningAtMs` read. Guarding it makes `start()` accept a
|
|
136
|
+
// row whose `state` is frozen — `structuredClone` + `Object.freeze`
|
|
137
|
+
// is an ordinary defensive rehydration — and that row is not usable
|
|
138
|
+
// by this class at all: `markRunning` writes the same field on every
|
|
139
|
+
// execution. Measured, the guard moves the failure from a throw out
|
|
140
|
+
// of `start()`, which the consumer's own `await` can catch, to a
|
|
141
|
+
// TypeError raised inside `onTimer`'s batch claim — a bare timer
|
|
142
|
+
// callback, so it surfaces as an unhandled rejection and is
|
|
143
|
+
// process-fatal under Node's default. Failing loudly at the store
|
|
144
|
+
// boundary is the better of the two, and the `finally` above keeps
|
|
145
|
+
// the rows that loaded before it scheduled.
|
|
146
|
+
job.state.runningAtMs = undefined;
|
|
147
|
+
this.jobs.set(job.id, job);
|
|
148
|
+
if (job.enabled && job.state.nextRunAtMs) {
|
|
149
|
+
this.heap.push({ key: job.id, nextTrigger: job.state.nextRunAtMs });
|
|
150
|
+
}
|
|
125
151
|
}
|
|
126
152
|
}
|
|
127
153
|
}
|
|
128
|
-
|
|
154
|
+
finally {
|
|
155
|
+
this.armTimer();
|
|
156
|
+
}
|
|
129
157
|
}
|
|
130
158
|
/**
|
|
131
159
|
* Stop the service. Clears timer.
|