@skelm/scheduler 0.4.1 → 0.4.2
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/scheduler.d.ts +13 -1
- package/dist/scheduler.js +32 -11
- package/package.json +2 -2
package/dist/scheduler.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare class Scheduler {
|
|
|
12
12
|
private webhookServer;
|
|
13
13
|
private pollJobs;
|
|
14
14
|
private queueJobs;
|
|
15
|
+
private readonly inFlight;
|
|
15
16
|
private isRunning;
|
|
16
17
|
private readonly runStore;
|
|
17
18
|
private readonly pipelineLoader;
|
|
@@ -39,8 +40,19 @@ export declare class Scheduler {
|
|
|
39
40
|
stopWebhookServer(): Promise<void>;
|
|
40
41
|
/** Start all triggers */
|
|
41
42
|
start(): Promise<void>;
|
|
42
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Stop all triggers. Clears the interval timers, then waits up to 30s
|
|
45
|
+
* for any in-flight executeTrigger callbacks to settle so a SIGTERM
|
|
46
|
+
* does not leave fire-and-forget executions racing the process exit.
|
|
47
|
+
*
|
|
48
|
+
* Runs unconditionally — `register()` arms timers immediately without
|
|
49
|
+
* setting isRunning, so a stop() that gated on isRunning would leak
|
|
50
|
+
* those timers when the scheduler is constructed without start().
|
|
51
|
+
*/
|
|
43
52
|
stop(): Promise<void>;
|
|
53
|
+
/** Track a fire-and-forget execution so stop() can drain it. */
|
|
54
|
+
private track;
|
|
55
|
+
private drainInFlight;
|
|
44
56
|
private startCronTrigger;
|
|
45
57
|
private stopCronTrigger;
|
|
46
58
|
private startIntervalTrigger;
|
package/dist/scheduler.js
CHANGED
|
@@ -11,6 +11,7 @@ export class Scheduler {
|
|
|
11
11
|
webhookServer = null;
|
|
12
12
|
pollJobs = new Map();
|
|
13
13
|
queueJobs = new Map();
|
|
14
|
+
inFlight = new Set();
|
|
14
15
|
isRunning = false;
|
|
15
16
|
runStore;
|
|
16
17
|
pipelineLoader;
|
|
@@ -154,10 +155,16 @@ export class Scheduler {
|
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* Stop all triggers. Clears the interval timers, then waits up to 30s
|
|
160
|
+
* for any in-flight executeTrigger callbacks to settle so a SIGTERM
|
|
161
|
+
* does not leave fire-and-forget executions racing the process exit.
|
|
162
|
+
*
|
|
163
|
+
* Runs unconditionally — `register()` arms timers immediately without
|
|
164
|
+
* setting isRunning, so a stop() that gated on isRunning would leak
|
|
165
|
+
* those timers when the scheduler is constructed without start().
|
|
166
|
+
*/
|
|
158
167
|
async stop() {
|
|
159
|
-
if (!this.isRunning)
|
|
160
|
-
return;
|
|
161
168
|
this.isRunning = false;
|
|
162
169
|
// Clear all jobs
|
|
163
170
|
for (const [id, job] of this.cronJobs) {
|
|
@@ -176,14 +183,28 @@ export class Scheduler {
|
|
|
176
183
|
clearInterval(job);
|
|
177
184
|
this.queueJobs.delete(id);
|
|
178
185
|
}
|
|
186
|
+
await this.drainInFlight(30_000);
|
|
187
|
+
}
|
|
188
|
+
/** Track a fire-and-forget execution so stop() can drain it. */
|
|
189
|
+
track(promise) {
|
|
190
|
+
this.inFlight.add(promise);
|
|
191
|
+
promise.finally(() => this.inFlight.delete(promise));
|
|
192
|
+
}
|
|
193
|
+
async drainInFlight(timeoutMs) {
|
|
194
|
+
if (this.inFlight.size === 0)
|
|
195
|
+
return;
|
|
196
|
+
await Promise.race([
|
|
197
|
+
Promise.allSettled([...this.inFlight]),
|
|
198
|
+
new Promise((resolve) => setTimeout(resolve, timeoutMs).unref?.()),
|
|
199
|
+
]);
|
|
179
200
|
}
|
|
180
201
|
startCronTrigger(trigger) {
|
|
181
202
|
// Simple cron implementation using setInterval
|
|
182
203
|
// In production, use 'cron' package for proper cron expression parsing
|
|
183
204
|
const intervalMs = this.parseCronToInterval(trigger.schedule);
|
|
184
|
-
const job = setInterval(
|
|
205
|
+
const job = setInterval(() => {
|
|
185
206
|
if (trigger.enabled && this.triggers.get(trigger.id)?.status === 'active') {
|
|
186
|
-
|
|
207
|
+
this.track(this.executeTrigger(trigger));
|
|
187
208
|
}
|
|
188
209
|
}, intervalMs);
|
|
189
210
|
this.cronJobs.set(trigger.id, job);
|
|
@@ -196,9 +217,9 @@ export class Scheduler {
|
|
|
196
217
|
}
|
|
197
218
|
}
|
|
198
219
|
startIntervalTrigger(trigger) {
|
|
199
|
-
const job = setInterval(
|
|
220
|
+
const job = setInterval(() => {
|
|
200
221
|
if (trigger.enabled && this.triggers.get(trigger.id)?.status === 'active') {
|
|
201
|
-
|
|
222
|
+
this.track(this.executeTrigger(trigger));
|
|
202
223
|
}
|
|
203
224
|
}, trigger.intervalMs);
|
|
204
225
|
this.intervalJobs.set(trigger.id, job);
|
|
@@ -211,9 +232,9 @@ export class Scheduler {
|
|
|
211
232
|
}
|
|
212
233
|
}
|
|
213
234
|
startPollTrigger(trigger) {
|
|
214
|
-
const job = setInterval(
|
|
235
|
+
const job = setInterval(() => {
|
|
215
236
|
if (trigger.enabled && this.triggers.get(trigger.id)?.status === 'active') {
|
|
216
|
-
|
|
237
|
+
this.track(this.executePollTrigger(trigger));
|
|
217
238
|
}
|
|
218
239
|
}, trigger.intervalMs);
|
|
219
240
|
this.pollJobs.set(trigger.id, job);
|
|
@@ -226,9 +247,9 @@ export class Scheduler {
|
|
|
226
247
|
}
|
|
227
248
|
}
|
|
228
249
|
startQueueTrigger(trigger) {
|
|
229
|
-
const job = setInterval(
|
|
250
|
+
const job = setInterval(() => {
|
|
230
251
|
if (trigger.enabled && this.triggers.get(trigger.id)?.status === 'active') {
|
|
231
|
-
|
|
252
|
+
this.track(this.executeQueueTrigger(trigger));
|
|
232
253
|
}
|
|
233
254
|
}, this.config.queuePollIntervalMs);
|
|
234
255
|
this.queueJobs.set(trigger.id, job);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skelm/scheduler",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Scott Glover <scottgl@gmail.com>",
|
|
6
6
|
"homepage": "https://skelm.dev/",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"test": "vitest run"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@skelm/core": "^0.4.
|
|
47
|
+
"@skelm/core": "^0.4.2"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/node": "^22.15.0",
|