sidekiq-ts 1.0.2 → 1.0.5
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/README.md +15 -3
- package/dist/abort-utils.d.ts +12 -0
- package/dist/abort-utils.d.ts.map +1 -0
- package/dist/abort-utils.js +32 -0
- package/dist/cli-helpers.d.ts.map +1 -1
- package/dist/cli-helpers.js +8 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +138 -34
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -3
- package/dist/iterable.js +2 -2
- package/dist/job.d.ts +7 -0
- package/dist/job.d.ts.map +1 -1
- package/dist/job.js +8 -0
- package/dist/leader.d.ts +0 -1
- package/dist/leader.d.ts.map +1 -1
- package/dist/leader.js +2 -19
- package/dist/periodic.d.ts +1 -0
- package/dist/periodic.d.ts.map +1 -1
- package/dist/periodic.js +6 -3
- package/dist/runner.d.ts +5 -2
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +58 -28
- package/dist/sidekiq.d.ts +9 -3
- package/dist/sidekiq.d.ts.map +1 -1
- package/dist/sidekiq.js +25 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,18 +85,23 @@ Inside `perform`, you can access:
|
|
|
85
85
|
|
|
86
86
|
```typescript
|
|
87
87
|
class MyJob extends Job<[string]> {
|
|
88
|
-
perform(data: string) {
|
|
88
|
+
async perform(data: string) {
|
|
89
89
|
// Unique job ID
|
|
90
90
|
console.log(this.jid);
|
|
91
91
|
|
|
92
92
|
// Check if worker is stopping (for graceful shutdown)
|
|
93
|
-
if (this.
|
|
93
|
+
if (this.interrupted()) {
|
|
94
94
|
return; // Exit early
|
|
95
95
|
}
|
|
96
|
+
|
|
97
|
+
// Get the AbortSignal for cancellation-aware APIs
|
|
98
|
+
const response = await fetch(url, { signal: this.signal });
|
|
96
99
|
}
|
|
97
100
|
}
|
|
98
101
|
```
|
|
99
102
|
|
|
103
|
+
The `signal` property returns an `AbortSignal` that is aborted when the worker is shutting down. Use it with `fetch()`, streams, database drivers, or any API that supports `AbortSignal` for graceful cancellation.
|
|
104
|
+
|
|
100
105
|
## Job Options
|
|
101
106
|
|
|
102
107
|
Configure jobs using the static `sidekiqOptions` property:
|
|
@@ -260,9 +265,16 @@ import { Sidekiq } from "sidekiq-ts";
|
|
|
260
265
|
import "./jobs/email-job.js";
|
|
261
266
|
import "./jobs/report-job.js";
|
|
262
267
|
|
|
268
|
+
// Start worker with automatic signal handling
|
|
269
|
+
const runner = await Sidekiq.run({ signals: true });
|
|
270
|
+
// SIGINT/SIGTERM will gracefully stop the worker
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The `signals: true` option registers handlers for `SIGINT`, `SIGTERM`, and `SIGTSTP` that gracefully shut down the worker. For custom signal handling:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
263
276
|
const runner = await Sidekiq.run();
|
|
264
277
|
|
|
265
|
-
// Handle shutdown signals
|
|
266
278
|
process.on("SIGTERM", async () => {
|
|
267
279
|
await runner.stop();
|
|
268
280
|
process.exit(0);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for AbortController-based cancellation.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Sleep that can be interrupted by an AbortSignal.
|
|
6
|
+
* Resolves immediately if signal is already aborted or when aborted during sleep.
|
|
7
|
+
*
|
|
8
|
+
* @param ms - Time to sleep in milliseconds
|
|
9
|
+
* @param signal - Optional AbortSignal to allow early cancellation
|
|
10
|
+
*/
|
|
11
|
+
export declare function sleepWithAbort(ms: number, signal: AbortSignal | undefined): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=abort-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abort-utils.d.ts","sourceRoot":"","sources":["../src/abort-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,IAAI,CAAC,CAqBf"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for AbortController-based cancellation.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Sleep that can be interrupted by an AbortSignal.
|
|
6
|
+
* Resolves immediately if signal is already aborted or when aborted during sleep.
|
|
7
|
+
*
|
|
8
|
+
* @param ms - Time to sleep in milliseconds
|
|
9
|
+
* @param signal - Optional AbortSignal to allow early cancellation
|
|
10
|
+
*/
|
|
11
|
+
export async function sleepWithAbort(ms, signal) {
|
|
12
|
+
if (!signal || signal.aborted) {
|
|
13
|
+
if (signal?.aborted) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
// No signal provided, fall back to regular sleep
|
|
17
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
await new Promise((resolve) => {
|
|
21
|
+
const timeout = setTimeout(() => {
|
|
22
|
+
signal.removeEventListener("abort", onAbort);
|
|
23
|
+
resolve();
|
|
24
|
+
}, ms);
|
|
25
|
+
const onAbort = () => {
|
|
26
|
+
clearTimeout(timeout);
|
|
27
|
+
signal.removeEventListener("abort", onAbort);
|
|
28
|
+
resolve();
|
|
29
|
+
};
|
|
30
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-helpers.d.ts","sourceRoot":"","sources":["../src/cli-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAWD,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,EAAE,KAAG,UAiH1C,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,SAKvC,CAAC;AASxB,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,EACd,SAAS,UAAU,EACnB,qBAAoB,MAAM,EAAO,KAChC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAuB1C,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,cAAc,EAAE,MAAM,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,qBAAqB,GAAI,MAAM,YAAY,KAAG,
|
|
1
|
+
{"version":3,"file":"cli-helpers.d.ts","sourceRoot":"","sources":["../src/cli-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAWD,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,EAAE,KAAG,UAiH1C,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,SAKvC,CAAC;AASxB,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,EACd,SAAS,UAAU,EACnB,qBAAoB,MAAM,EAAO,KAChC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAuB1C,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,cAAc,EAAE,MAAM,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,qBAAqB,GAAI,MAAM,YAAY,KAAG,eA0B1D,CAAC"}
|
package/dist/cli-helpers.js
CHANGED
|
@@ -152,13 +152,21 @@ export const applyCliOptions = (config, options, loadedRequirePaths = []) => {
|
|
|
152
152
|
};
|
|
153
153
|
export const createShutdownHandler = (deps) => {
|
|
154
154
|
let shuttingDown = false;
|
|
155
|
+
let shutdownStartedAt = 0;
|
|
155
156
|
const handler = async (signal) => {
|
|
156
157
|
if (shuttingDown) {
|
|
158
|
+
// Only force exit if more than 500ms has passed since first signal.
|
|
159
|
+
// This prevents duplicate signals from a single Ctrl+C from force-exiting.
|
|
160
|
+
const elapsed = Date.now() - shutdownStartedAt;
|
|
161
|
+
if (elapsed < 500) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
157
164
|
deps.logger.info(() => `Received ${signal}, forcing exit`);
|
|
158
165
|
deps.exit(1);
|
|
159
166
|
return;
|
|
160
167
|
}
|
|
161
168
|
shuttingDown = true;
|
|
169
|
+
shutdownStartedAt = Date.now();
|
|
162
170
|
deps.logger.info(() => `Received ${signal}, shutting down`);
|
|
163
171
|
await deps.stop();
|
|
164
172
|
deps.exit(0);
|
package/dist/client.d.ts
CHANGED
|
@@ -20,6 +20,18 @@ export declare class Client {
|
|
|
20
20
|
static enqueueToIn<TArgs extends unknown[]>(queue: string, interval: number, klass: JobClassLike, ...args: TArgs): Promise<string | null>;
|
|
21
21
|
static enqueueIn<TArgs extends unknown[]>(interval: number, klass: JobClassLike, ...args: TArgs): Promise<string | null>;
|
|
22
22
|
static via<T>(redis: RedisClient, fn: () => Promise<T>): Promise<T>;
|
|
23
|
+
/**
|
|
24
|
+
* Returns the number of jobs currently in the local queue.
|
|
25
|
+
* Jobs are queued locally when Redis is unavailable.
|
|
26
|
+
*/
|
|
27
|
+
static localQueueSize(): number;
|
|
28
|
+
/**
|
|
29
|
+
* Clears the local queue. Primarily for testing.
|
|
30
|
+
*/
|
|
31
|
+
static clearLocalQueue(): void;
|
|
23
32
|
private rawPush;
|
|
33
|
+
private pushToRedis;
|
|
34
|
+
private drainLocalQueue;
|
|
35
|
+
private queueLocally;
|
|
24
36
|
}
|
|
25
37
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAS1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAS1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAOxE,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAc;gBAE/B,EACV,MAAM,EACN,KAAK,GACN,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,WAAW,CAAA;KAAO;YAKlC,QAAQ;IAQtB,UAAU,CACR,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,kBAAkB,CAAC,KAAK,IAAI,GAC/C,MAAM,CAAC,kBAAkB,CAAC;IAOvB,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA6B9C,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAkIxD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAc3C,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIrD,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAI/D,MAAM,CAAC,OAAO,CAAC,KAAK,SAAS,OAAO,EAAE,EACpC,KAAK,EAAE,YAAY,EACnB,GAAG,IAAI,EAAE,KAAK,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIzB,MAAM,CAAC,SAAS,CAAC,KAAK,SAAS,OAAO,EAAE,EACtC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,YAAY,EACnB,GAAG,IAAI,EAAE,KAAK,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIzB,MAAM,CAAC,WAAW,CAAC,KAAK,SAAS,OAAO,EAAE,EACxC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,GAAG,IAAI,EAAE,KAAK,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUzB,MAAM,CAAC,SAAS,CAAC,KAAK,SAAS,OAAO,EAAE,EACtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,GAAG,IAAI,EAAE,KAAK,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQzB,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAInE;;;OAGG;IACH,MAAM,CAAC,cAAc,IAAI,MAAM;IAI/B;;OAEG;IACH,MAAM,CAAC,eAAe,IAAI,IAAI;YAIhB,OAAO;YAgCP,WAAW;YAuCX,eAAe;IAgC7B,OAAO,CAAC,YAAY;CAgCrB"}
|
package/dist/client.js
CHANGED
|
@@ -5,6 +5,8 @@ import { dumpJson } from "./json.js";
|
|
|
5
5
|
import { Sidekiq } from "./sidekiq.js";
|
|
6
6
|
import { Testing } from "./testing.js";
|
|
7
7
|
const redisContext = new AsyncLocalStorage();
|
|
8
|
+
// Local queue for reliable push - holds jobs when Redis is unavailable
|
|
9
|
+
const localQueue = [];
|
|
8
10
|
export class Client {
|
|
9
11
|
config;
|
|
10
12
|
redisClient;
|
|
@@ -26,15 +28,23 @@ export class Client {
|
|
|
26
28
|
async push(item) {
|
|
27
29
|
const normalized = normalizeItem(item, Sidekiq.defaultJobOptions());
|
|
28
30
|
const queue = normalized.queue ?? "default";
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
try {
|
|
32
|
+
const redis = await this.getRedis();
|
|
33
|
+
const result = await this.config.clientMiddleware.invoke(item.class, normalized, queue, redis, async () => normalized);
|
|
34
|
+
if (!result) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const payload = result;
|
|
38
|
+
verifyJson(payload.args, this.config.strictArgs);
|
|
39
|
+
await this.rawPush([payload]);
|
|
40
|
+
return payload.jid ?? null;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
// Redis unavailable - queue locally (skipping middleware)
|
|
44
|
+
verifyJson(normalized.args, this.config.strictArgs);
|
|
45
|
+
this.queueLocally([normalized], error);
|
|
46
|
+
return normalized.jid ?? null;
|
|
33
47
|
}
|
|
34
|
-
const payload = result;
|
|
35
|
-
verifyJson(payload.args, this.config.strictArgs);
|
|
36
|
-
await this.rawPush([payload]);
|
|
37
|
-
return payload.jid ?? null;
|
|
38
48
|
}
|
|
39
49
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: bulk push validation is inherently complex
|
|
40
50
|
async pushBulk(items) {
|
|
@@ -82,37 +92,59 @@ export class Client {
|
|
|
82
92
|
base.jid = undefined;
|
|
83
93
|
const normalized = normalizeItem(base, Sidekiq.defaultJobOptions());
|
|
84
94
|
const results = [];
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
try {
|
|
96
|
+
const redis = await this.getRedis();
|
|
97
|
+
for (let i = 0; i < args.length; i += batchSize) {
|
|
98
|
+
const slice = args.slice(i, i + batchSize);
|
|
99
|
+
if (slice.length === 0) {
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
if (!slice.every((entry) => Array.isArray(entry))) {
|
|
103
|
+
throw new Error("Bulk arguments must be an Array of Arrays: [[1], [2]]");
|
|
104
|
+
}
|
|
105
|
+
const payloads = await Promise.all(slice.map(async (jobArgs, index) => {
|
|
106
|
+
const payload = {
|
|
107
|
+
...normalized,
|
|
108
|
+
args: jobArgs,
|
|
109
|
+
jid: generateJid(),
|
|
110
|
+
};
|
|
111
|
+
if (resolvedAt !== undefined) {
|
|
112
|
+
payload.at = Array.isArray(resolvedAt)
|
|
113
|
+
? resolvedAt[i + index]
|
|
114
|
+
: resolvedAt;
|
|
115
|
+
}
|
|
116
|
+
const result = await this.config.clientMiddleware.invoke(items.class, payload, payload.queue ?? "default", redis, async () => payload);
|
|
117
|
+
if (!result) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const finalPayload = result;
|
|
121
|
+
verifyJson(finalPayload.args, this.config.strictArgs);
|
|
122
|
+
return finalPayload;
|
|
123
|
+
}));
|
|
124
|
+
const toPush = payloads.filter((payload) => Boolean(payload));
|
|
125
|
+
await this.rawPush(toPush);
|
|
126
|
+
results.push(...payloads.map((payload) => payload?.jid ?? null));
|
|
93
127
|
}
|
|
94
|
-
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
// Redis unavailable - build payloads without middleware and queue locally
|
|
131
|
+
for (let i = results.length; i < args.length; i++) {
|
|
132
|
+
const jobArgs = args[i];
|
|
133
|
+
if (!Array.isArray(jobArgs)) {
|
|
134
|
+
throw new Error("Bulk arguments must be an Array of Arrays: [[1], [2]]");
|
|
135
|
+
}
|
|
95
136
|
const payload = {
|
|
96
137
|
...normalized,
|
|
97
138
|
args: jobArgs,
|
|
98
139
|
jid: generateJid(),
|
|
99
140
|
};
|
|
100
141
|
if (resolvedAt !== undefined) {
|
|
101
|
-
payload.at = Array.isArray(resolvedAt)
|
|
102
|
-
? resolvedAt[i + index]
|
|
103
|
-
: resolvedAt;
|
|
104
|
-
}
|
|
105
|
-
const result = await this.config.clientMiddleware.invoke(items.class, payload, payload.queue ?? "default", redis, async () => payload);
|
|
106
|
-
if (!result) {
|
|
107
|
-
return null;
|
|
142
|
+
payload.at = Array.isArray(resolvedAt) ? resolvedAt[i] : resolvedAt;
|
|
108
143
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
const toPush = payloads.filter((payload) => Boolean(payload));
|
|
114
|
-
await this.rawPush(toPush);
|
|
115
|
-
results.push(...payloads.map((payload) => payload?.jid ?? null));
|
|
144
|
+
verifyJson(payload.args, this.config.strictArgs);
|
|
145
|
+
this.queueLocally([payload], error);
|
|
146
|
+
results.push(payload.jid ?? null);
|
|
147
|
+
}
|
|
116
148
|
}
|
|
117
149
|
return results;
|
|
118
150
|
}
|
|
@@ -123,7 +155,7 @@ export class Client {
|
|
|
123
155
|
const pipeline = redis.multi();
|
|
124
156
|
pipeline.hSetNX(key, "cancelled", now);
|
|
125
157
|
pipeline.hGet(key, "cancelled");
|
|
126
|
-
pipeline.expire(key, ITERATION_STATE_TTL_SECONDS
|
|
158
|
+
pipeline.expire(key, ITERATION_STATE_TTL_SECONDS);
|
|
127
159
|
const result = await pipeline.exec();
|
|
128
160
|
const cancelled = result?.[1];
|
|
129
161
|
return Boolean(Number(cancelled));
|
|
@@ -159,7 +191,19 @@ export class Client {
|
|
|
159
191
|
static via(redis, fn) {
|
|
160
192
|
return redisContext.run(redis, fn);
|
|
161
193
|
}
|
|
162
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Returns the number of jobs currently in the local queue.
|
|
196
|
+
* Jobs are queued locally when Redis is unavailable.
|
|
197
|
+
*/
|
|
198
|
+
static localQueueSize() {
|
|
199
|
+
return localQueue.length;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Clears the local queue. Primarily for testing.
|
|
203
|
+
*/
|
|
204
|
+
static clearLocalQueue() {
|
|
205
|
+
localQueue.length = 0;
|
|
206
|
+
}
|
|
163
207
|
async rawPush(payloads) {
|
|
164
208
|
if (payloads.length === 0) {
|
|
165
209
|
return;
|
|
@@ -177,7 +221,19 @@ export class Client {
|
|
|
177
221
|
}
|
|
178
222
|
return;
|
|
179
223
|
}
|
|
180
|
-
|
|
224
|
+
try {
|
|
225
|
+
const redis = await this.getRedis();
|
|
226
|
+
// Drain any locally queued jobs first
|
|
227
|
+
await this.drainLocalQueue(redis);
|
|
228
|
+
// Push the current batch
|
|
229
|
+
await this.pushToRedis(redis, payloads);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
// Queue locally on Redis failure (silent success)
|
|
233
|
+
this.queueLocally(payloads, error);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async pushToRedis(redis, payloads) {
|
|
181
237
|
const pipeline = redis.multi();
|
|
182
238
|
if (payloads[0].at !== undefined) {
|
|
183
239
|
for (const payload of payloads) {
|
|
@@ -209,4 +265,52 @@ export class Client {
|
|
|
209
265
|
}
|
|
210
266
|
await pipeline.exec();
|
|
211
267
|
}
|
|
268
|
+
async drainLocalQueue(redis) {
|
|
269
|
+
if (localQueue.length === 0) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const drainCount = localQueue.length;
|
|
273
|
+
this.config.logger.info(() => `Draining ${drainCount} locally queued jobs to Redis`);
|
|
274
|
+
// Process in batches to avoid huge pipelines
|
|
275
|
+
while (localQueue.length > 0) {
|
|
276
|
+
const batch = localQueue.splice(0, 100);
|
|
277
|
+
try {
|
|
278
|
+
// Separate scheduled and immediate jobs
|
|
279
|
+
const scheduled = batch.filter((p) => p.at !== undefined);
|
|
280
|
+
const immediate = batch.filter((p) => p.at === undefined);
|
|
281
|
+
if (scheduled.length > 0) {
|
|
282
|
+
await this.pushToRedis(redis, scheduled);
|
|
283
|
+
}
|
|
284
|
+
if (immediate.length > 0) {
|
|
285
|
+
await this.pushToRedis(redis, immediate);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// Put back and abort drain - Redis still unavailable
|
|
290
|
+
localQueue.unshift(...batch);
|
|
291
|
+
throw new Error("Failed to drain local queue");
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
queueLocally(payloads, error) {
|
|
296
|
+
const maxQueue = this.config.reliableClientMaxQueue;
|
|
297
|
+
if (maxQueue === 0) {
|
|
298
|
+
// Reliability disabled - re-throw
|
|
299
|
+
throw error;
|
|
300
|
+
}
|
|
301
|
+
const available = maxQueue - localQueue.length;
|
|
302
|
+
if (available <= 0) {
|
|
303
|
+
this.config.logger.warn(() => `Local queue full (${maxQueue} jobs), dropping ${payloads.length} jobs`);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const toQueue = payloads.slice(0, available);
|
|
307
|
+
localQueue.push(...toQueue);
|
|
308
|
+
const dropped = payloads.length - toQueue.length;
|
|
309
|
+
if (dropped > 0) {
|
|
310
|
+
this.config.logger.warn(() => `Queued ${toQueue.length} jobs locally, dropped ${dropped} (queue full at ${maxQueue})`);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
this.config.logger.warn(() => `Redis unavailable, queued ${toQueue.length} jobs locally (${localQueue.length}/${maxQueue})`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
212
316
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export declare class Config {
|
|
|
36
36
|
], JobPayload | false | null | undefined>;
|
|
37
37
|
serverMiddleware: MiddlewareChain<[unknown, JobPayload, string], unknown>;
|
|
38
38
|
leaderElection: LeaderElectionConfig;
|
|
39
|
+
reliableClientMaxQueue: number;
|
|
39
40
|
private redisClient?;
|
|
40
41
|
constructor(options?: ConfigOptions);
|
|
41
42
|
getRedisClient(): Promise<RedisClient>;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAIhD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,UAAU,EACV,oBAAoB,EACpB,eAAe,EACf,cAAc,EACf,MAAM,YAAY,CAAC;AAapB,qBAAa,MAAM;IACjB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,4BAA4B,EAAE,MAAM,CAAC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;IACpD,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,eAAe,EAAE,eAAe,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,SAAS,CAAC;IACrB,gBAAgB,EAAE,eAAe,CAC/B;QAAC,MAAM,GAAG,OAAO;QAAE,UAAU;QAAE,MAAM;QAAE,WAAW;KAAC,EACnD,UAAU,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CACtC,CAAC;IACF,gBAAgB,EAAE,eAAe,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;IAC1E,cAAc,EAAE,oBAAoB,CAAC;IACrC,OAAO,CAAC,WAAW,CAAC,CAAc;gBAGtB,OAAO,GAAE,aAAkB;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAIhD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,UAAU,EACV,oBAAoB,EACpB,eAAe,EACf,cAAc,EACf,MAAM,YAAY,CAAC;AAapB,qBAAa,MAAM;IACjB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,4BAA4B,EAAE,MAAM,CAAC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;IACpD,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,eAAe,EAAE,eAAe,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,SAAS,CAAC;IACrB,gBAAgB,EAAE,eAAe,CAC/B;QAAC,MAAM,GAAG,OAAO;QAAE,UAAU;QAAE,MAAM;QAAE,WAAW;KAAC,EACnD,UAAU,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CACtC,CAAC;IACF,gBAAgB,EAAE,eAAe,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;IAC1E,cAAc,EAAE,oBAAoB,CAAC;IACrC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,OAAO,CAAC,WAAW,CAAC,CAAc;gBAGtB,OAAO,GAAE,aAAkB;IAiDjC,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAatC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,UAAU,IAAI,MAAM,EAAE;IAatB;;;;;;;;OAQG;IACG,SAAS,CACb,KAAK,EAAE,MAAM,eAAe,EAC5B,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAO,GACxE,OAAO,CAAC,IAAI,CAAC;CA2BjB"}
|
package/dist/config.js
CHANGED
|
@@ -41,6 +41,7 @@ export class Config {
|
|
|
41
41
|
clientMiddleware;
|
|
42
42
|
serverMiddleware;
|
|
43
43
|
leaderElection;
|
|
44
|
+
reliableClientMaxQueue;
|
|
44
45
|
redisClient;
|
|
45
46
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: config initialization requires many defaults
|
|
46
47
|
constructor(options = {}) {
|
|
@@ -79,6 +80,7 @@ export class Config {
|
|
|
79
80
|
this.clientMiddleware = new MiddlewareChain(this);
|
|
80
81
|
this.serverMiddleware = new MiddlewareChain(this);
|
|
81
82
|
this.leaderElection = options.leaderElection ?? {};
|
|
83
|
+
this.reliableClientMaxQueue = options.reliableClientMaxQueue ?? 1000;
|
|
82
84
|
if (this.errorHandlers.length === 0) {
|
|
83
85
|
this.errorHandlers.push((error, context) => {
|
|
84
86
|
Context.with(context, () => {
|
|
@@ -129,9 +131,6 @@ export class Config {
|
|
|
129
131
|
*/
|
|
130
132
|
async fireEvent(event, options = {}) {
|
|
131
133
|
const { oneshot = true, reverse = false, reraise = false } = options;
|
|
132
|
-
if (oneshot) {
|
|
133
|
-
this.logger.debug(() => `Firing ${event} event`);
|
|
134
|
-
}
|
|
135
134
|
const handlers = [...this.lifecycleEvents[event]];
|
|
136
135
|
if (reverse) {
|
|
137
136
|
handlers.reverse();
|
package/dist/iterable.js
CHANGED
|
@@ -42,7 +42,7 @@ export class IterableJob extends Job {
|
|
|
42
42
|
const pipeline = redis.multi();
|
|
43
43
|
pipeline.hSetNX(key, "cancelled", String(now));
|
|
44
44
|
pipeline.hGet(key, "cancelled");
|
|
45
|
-
pipeline.expire(key, ITERATION_STATE_TTL_SECONDS
|
|
45
|
+
pipeline.expire(key, ITERATION_STATE_TTL_SECONDS);
|
|
46
46
|
const result = await pipeline.exec();
|
|
47
47
|
const cancelled = result?.[1] ?? null;
|
|
48
48
|
this.cancelledValue = cancelled ? Number(cancelled) : null;
|
|
@@ -269,7 +269,7 @@ export class IterableJob extends Job {
|
|
|
269
269
|
};
|
|
270
270
|
const pipeline = redis.multi();
|
|
271
271
|
pipeline.hSet(key, state);
|
|
272
|
-
pipeline.expire(key, ITERATION_STATE_TTL_SECONDS
|
|
272
|
+
pipeline.expire(key, ITERATION_STATE_TTL_SECONDS);
|
|
273
273
|
pipeline.hGet(key, "cancelled");
|
|
274
274
|
const result = await pipeline.exec();
|
|
275
275
|
const cancelled = result?.[2] ?? null;
|
package/dist/job.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export declare abstract class Job<TArgs extends unknown[] = unknown[]> {
|
|
|
31
31
|
jid?: string;
|
|
32
32
|
_context?: {
|
|
33
33
|
stopping: () => boolean;
|
|
34
|
+
signal?: AbortSignal;
|
|
34
35
|
};
|
|
35
36
|
static getSidekiqOptions(this: JobConstructor): JobOptions;
|
|
36
37
|
static setSidekiqOptions(this: JobConstructor, options: JobOptions): void;
|
|
@@ -53,6 +54,12 @@ export declare abstract class Job<TArgs extends unknown[] = unknown[]> {
|
|
|
53
54
|
static clientPush(this: JobConstructor, item: JobPayload): Promise<string | null>;
|
|
54
55
|
logger(): import("./logger.js").Logger;
|
|
55
56
|
interrupted(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Get the AbortSignal for this job.
|
|
59
|
+
* Can be used with fetch(), streams, or any AbortSignal-compatible API.
|
|
60
|
+
* The signal is aborted when the runner is shutting down.
|
|
61
|
+
*/
|
|
62
|
+
get signal(): AbortSignal | undefined;
|
|
56
63
|
abstract perform(...args: TArgs): Promise<void> | void;
|
|
57
64
|
}
|
|
58
65
|
export type RetryInHandler = (count: number, error: Error, payload: JobPayload) => number | "discard" | "kill" | "default";
|
package/dist/job.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAKpB,MAAM,MAAM,cAAc,CAAC,KAAK,SAAS,OAAO,EAAE,GAAG,OAAO,EAAE,IAAI;IAChE,QAAQ,GAAG,CAAC,KAAK,CAAC,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,iBAAiB,IAAI,UAAU,CAAC;IAChC,iBAAiB,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IAC7C,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACzD,GAAG,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACjD,YAAY,CAAC,GAAG,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACrD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACpE,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACrE,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9E,aAAa,CAAC,GAAG,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACvD,IAAI,IAAI,UAAU,EAAE,CAAC;IACrB,KAAK,IAAI,IAAI,CAAC;IACd,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACtD,CAAC;AAIF,KAAK,QAAQ,GAAG,QAAQ,WAAW,GAAG,CAAC,GAAG,CAAC,CAAC;AAC5C,KAAK,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,QAAQ,GAAG,CAAC,MAAM,KAAK,CAAC,CAAA;CAAE,GAC7D,KAAK,GACL,OAAO,EAAE,CAAC;AAEd,8BAAsB,GAAG,CAAC,KAAK,SAAS,OAAO,EAAE,GAAG,OAAO,EAAE;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"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"}
|
package/dist/job.js
CHANGED
|
@@ -112,6 +112,14 @@ export class Job {
|
|
|
112
112
|
interrupted() {
|
|
113
113
|
return this._context?.stopping() ?? false;
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Get the AbortSignal for this job.
|
|
117
|
+
* Can be used with fetch(), streams, or any AbortSignal-compatible API.
|
|
118
|
+
* The signal is aborted when the runner is shutting down.
|
|
119
|
+
*/
|
|
120
|
+
get signal() {
|
|
121
|
+
return this._context?.signal;
|
|
122
|
+
}
|
|
115
123
|
}
|
|
116
124
|
export class JobSetter {
|
|
117
125
|
klass;
|
package/dist/leader.d.ts
CHANGED
package/dist/leader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"leader.d.ts","sourceRoot":"","sources":["../src/leader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"leader.d.ts","sourceRoot":"","sources":["../src/leader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAe1C,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B,OAAO,CAAC,KAAK,CAAC,CAAc;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAC,CAAgB;IACpC,OAAO,CAAC,cAAc,CAAC,CAAkB;gBAE7B,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB;IAS9D;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B3B;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAK7C;;OAEG;YACW,UAAU;IA4BxB;;OAEG;YACW,UAAU;IAmBxB;;OAEG;YACW,UAAU;CAiCzB"}
|
package/dist/leader.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - 20 second TTL on leader key for automatic failover
|
|
9
9
|
*/
|
|
10
10
|
import { hostname } from "node:os";
|
|
11
|
+
import { sleepWithAbort } from "./abort-utils.js";
|
|
11
12
|
const LEADER_KEY = "leader";
|
|
12
13
|
const LEADER_TTL_SECONDS = 20;
|
|
13
14
|
const LEADER_REFRESH_INTERVAL_MS = 15_000;
|
|
@@ -169,25 +170,7 @@ export class LeaderElector {
|
|
|
169
170
|
const interval = this.isLeader
|
|
170
171
|
? this.refreshInterval
|
|
171
172
|
: this.checkInterval;
|
|
172
|
-
await
|
|
173
|
+
await sleepWithAbort(interval, this.stopController?.signal);
|
|
173
174
|
}
|
|
174
175
|
}
|
|
175
|
-
async sleepWithAbort(ms) {
|
|
176
|
-
const controller = this.stopController;
|
|
177
|
-
if (!controller || controller.signal.aborted) {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
await new Promise((resolve) => {
|
|
181
|
-
const timeout = setTimeout(() => {
|
|
182
|
-
controller.signal.removeEventListener("abort", onAbort);
|
|
183
|
-
resolve();
|
|
184
|
-
}, ms);
|
|
185
|
-
const onAbort = () => {
|
|
186
|
-
clearTimeout(timeout);
|
|
187
|
-
controller.signal.removeEventListener("abort", onAbort);
|
|
188
|
-
resolve();
|
|
189
|
-
};
|
|
190
|
-
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
176
|
}
|
package/dist/periodic.d.ts
CHANGED
package/dist/periodic.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"periodic.d.ts","sourceRoot":"","sources":["../src/periodic.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"periodic.d.ts","sourceRoot":"","sources":["../src/periodic.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,oBAAoB;IACpB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,6BAA6B;IAC7B,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAQD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,OAAO,CAAC,KAAK,CAAC,CAAc;IAC5B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAC,CAAgB;IACpC,OAAO,CAAC,cAAc,CAAC,CAAkB;gBAE7B,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa;IAMxD;;;;;;;OAOG;IACH,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE,kBAAuB,GAC/B,MAAM;IA2BT;;OAEG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAQhC;;OAEG;IACH,IAAI,IAAI,iBAAiB,EAAE;IAI3B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB3B;;OAEG;IACH,OAAO,CAAC,WAAW;IAiBnB;;OAEG;YACW,WAAW;IAoBzB;;OAEG;YAEW,YAAY;IA2C1B;;OAEG;YACW,kBAAkB;CAmCjC"}
|
package/dist/periodic.js
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* Allows registering jobs that run on a cron schedule.
|
|
5
5
|
* Only the leader process enqueues periodic jobs to prevent duplicates.
|
|
6
6
|
*/
|
|
7
|
+
import { sleepWithAbort } from "./abort-utils.js";
|
|
7
8
|
import { Client } from "./client.js";
|
|
8
9
|
import { parseCron, shouldRunAt } from "./cron.js";
|
|
9
10
|
import { dumpJson } from "./json.js";
|
|
10
11
|
const CRON_KEY = "cron";
|
|
11
12
|
const CRON_LOCK_PREFIX = "cron:lock:";
|
|
12
13
|
const CRON_LOCK_TTL_SECONDS = 60;
|
|
13
|
-
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
14
14
|
export class PeriodicScheduler {
|
|
15
15
|
config;
|
|
16
16
|
leaderElector;
|
|
@@ -19,6 +19,7 @@ export class PeriodicScheduler {
|
|
|
19
19
|
redis;
|
|
20
20
|
running = false;
|
|
21
21
|
loopPromise;
|
|
22
|
+
stopController;
|
|
22
23
|
constructor(config, leaderElector) {
|
|
23
24
|
this.config = config;
|
|
24
25
|
this.leaderElector = leaderElector;
|
|
@@ -75,6 +76,7 @@ export class PeriodicScheduler {
|
|
|
75
76
|
}
|
|
76
77
|
this.redis = await this.config.getRedisClient();
|
|
77
78
|
this.running = true;
|
|
79
|
+
this.stopController = new AbortController();
|
|
78
80
|
// Persist job configs to Redis for visibility
|
|
79
81
|
await this.syncToRedis();
|
|
80
82
|
// Start the background loop
|
|
@@ -88,6 +90,7 @@ export class PeriodicScheduler {
|
|
|
88
90
|
return;
|
|
89
91
|
}
|
|
90
92
|
this.running = false;
|
|
93
|
+
this.stopController?.abort();
|
|
91
94
|
// Remove job configs from Redis
|
|
92
95
|
if (this.redis && this.jobs.size > 0) {
|
|
93
96
|
try {
|
|
@@ -141,14 +144,14 @@ export class PeriodicScheduler {
|
|
|
141
144
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: scheduler loop requires multiple conditional checks
|
|
142
145
|
async periodicLoop() {
|
|
143
146
|
// Wait for initial sync with jitter to avoid thundering herd
|
|
144
|
-
await
|
|
147
|
+
await sleepWithAbort(Math.random() * 5000, this.stopController?.signal);
|
|
145
148
|
while (this.running) {
|
|
146
149
|
// Wait until the next minute boundary plus jitter
|
|
147
150
|
const now = Date.now();
|
|
148
151
|
const nextMinute = Math.ceil(now / 60_000) * 60_000;
|
|
149
152
|
const jitter = Math.random() * 2000; // 0-2 seconds
|
|
150
153
|
const waitMs = Math.max(0, nextMinute - now + jitter);
|
|
151
|
-
await
|
|
154
|
+
await sleepWithAbort(waitMs, this.stopController?.signal);
|
|
152
155
|
if (!this.running) {
|
|
153
156
|
break;
|
|
154
157
|
}
|
package/dist/runner.d.ts
CHANGED
|
@@ -14,9 +14,11 @@ export declare class Runner {
|
|
|
14
14
|
private quieting;
|
|
15
15
|
private stopping;
|
|
16
16
|
private readonly workers;
|
|
17
|
-
private schedulerHandle?;
|
|
18
17
|
private schedulerRunning;
|
|
19
|
-
private
|
|
18
|
+
private schedulerController?;
|
|
19
|
+
private heartbeatController?;
|
|
20
|
+
private heartbeatLoopPromise?;
|
|
21
|
+
private jobController?;
|
|
20
22
|
private readonly queueStrategy;
|
|
21
23
|
private baseRedis?;
|
|
22
24
|
private readonly workerRedis;
|
|
@@ -63,6 +65,7 @@ export declare class Runner {
|
|
|
63
65
|
private runWithProfiler;
|
|
64
66
|
private cleanupProcesses;
|
|
65
67
|
private startHeartbeat;
|
|
68
|
+
private heartbeatLoop;
|
|
66
69
|
private stopHeartbeat;
|
|
67
70
|
private enqueueScheduled;
|
|
68
71
|
private workLoop;
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAU1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAU1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA8G7C,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,mBAAmB,CAAC,CAAkB;IAC9C,OAAO,CAAC,mBAAmB,CAAC,CAAkB;IAC9C,OAAO,CAAC,oBAAoB,CAAC,CAAgB;IAC7C,OAAO,CAAC,aAAa,CAAC,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,SAAS,CAAC,CAAgD;IAClE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAEnB;IACT,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAGtB;IACJ,OAAO,CAAC,QAAQ,CAAC,UAAU,CAGvB;IACJ,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;IAChD,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,kBAAkB,CAAC,CAAoB;gBAEnC,MAAM,EAAE,MAAM;IAQpB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA0D3B;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,IAAI,iBAAiB,IAAI,iBAAiB,GAAG,SAAS,CAErD;IAED,YAAY,IAAI,YAAY,EAAE;IAsB9B,OAAO,CAAC,cAAc;YAMR,gBAAgB;YAWhB,WAAW;YAuBX,YAAY;IAM1B,OAAO,CAAC,kBAAkB;YAIZ,kBAAkB;IAqBhC,OAAO,CAAC,aAAa;YAKP,SAAS;YA0DT,YAAY;IAoB1B,OAAO,CAAC,aAAa;YAkBP,cAAc;IAa5B,OAAO,CAAC,WAAW;YAcL,QAAQ;IAWtB,OAAO,CAAC,SAAS;YAoBH,YAAY;YAMZ,cAAc;YAQd,iBAAiB;YAkBjB,eAAe;YAWf,eAAe;YAWf,gBAAgB;IA8B9B,OAAO,CAAC,cAAc;YAKR,aAAa;IAW3B,OAAO,CAAC,aAAa;YAIP,gBAAgB;YA8BhB,QAAQ;YAwCR,SAAS;YA4BT,UAAU;YA2EV,aAAa;IA2H3B,OAAO,CAAC,WAAW;YAoBL,gBAAgB;YA8BhB,YAAY;YAYZ,gBAAgB;YAmBhB,gBAAgB;IAsB9B,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,gBAAgB;YAQV,UAAU;CAczB"}
|
package/dist/runner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { hostname } from "node:os";
|
|
2
|
+
import { sleepWithAbort } from "./abort-utils.js";
|
|
2
3
|
import { compressBacktrace, extractBacktrace } from "./backtrace.js";
|
|
3
|
-
import { Client } from "./client.js";
|
|
4
4
|
import { ensureInterruptHandler } from "./interrupt-handler.js";
|
|
5
5
|
import { JobSkipError } from "./iterable-errors.js";
|
|
6
6
|
import { dumpJson, loadJson } from "./json.js";
|
|
@@ -11,11 +11,19 @@ const FETCH_TIMEOUT_SECONDS = 2;
|
|
|
11
11
|
const STATS_TTL_SECONDS = 5 * 365 * 24 * 60 * 60;
|
|
12
12
|
const INITIAL_WAIT_SECONDS = 10;
|
|
13
13
|
const PAUSE_TIME_MS = 500;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
// Reliable scheduler: atomically pop from sorted set AND enqueue to queue
|
|
15
|
+
// This eliminates the data loss window between pop and push operations
|
|
16
|
+
const LUA_RELIABLE_ENQUEUE = `
|
|
17
|
+
local key, now_seconds, now_millis = KEYS[1], ARGV[1], ARGV[2]
|
|
18
|
+
local jobs = redis.call("zrange", key, "-inf", now_seconds, "byscore", "limit", 0, 1)
|
|
17
19
|
if jobs[1] then
|
|
18
20
|
redis.call("zrem", key, jobs[1])
|
|
21
|
+
local job = cjson.decode(jobs[1])
|
|
22
|
+
local queue = job.queue or "default"
|
|
23
|
+
job.enqueued_at = tonumber(now_millis)
|
|
24
|
+
local payload = cjson.encode(job)
|
|
25
|
+
redis.call("sadd", "queues", queue)
|
|
26
|
+
redis.call("lpush", "queue:" .. queue, payload)
|
|
19
27
|
return jobs[1]
|
|
20
28
|
end
|
|
21
29
|
`;
|
|
@@ -99,9 +107,11 @@ export class Runner {
|
|
|
99
107
|
quieting = false;
|
|
100
108
|
stopping = false;
|
|
101
109
|
workers = [];
|
|
102
|
-
schedulerHandle;
|
|
103
110
|
schedulerRunning = false;
|
|
104
|
-
|
|
111
|
+
schedulerController;
|
|
112
|
+
heartbeatController;
|
|
113
|
+
heartbeatLoopPromise;
|
|
114
|
+
jobController;
|
|
105
115
|
queueStrategy;
|
|
106
116
|
baseRedis;
|
|
107
117
|
workerRedis = [];
|
|
@@ -123,6 +133,7 @@ export class Runner {
|
|
|
123
133
|
}
|
|
124
134
|
async start() {
|
|
125
135
|
this.baseRedis = await this.config.getRedisClient();
|
|
136
|
+
this.jobController = new AbortController();
|
|
126
137
|
ensureInterruptHandler(this.config);
|
|
127
138
|
// Start leader election
|
|
128
139
|
this.leaderElector = new LeaderElector(this.config, {
|
|
@@ -153,14 +164,19 @@ export class Runner {
|
|
|
153
164
|
await this.quiet();
|
|
154
165
|
}
|
|
155
166
|
this.stopping = true;
|
|
167
|
+
this.jobController?.abort();
|
|
156
168
|
await this.config.fireEvent("shutdown", { reverse: true });
|
|
157
169
|
// Stop periodic scheduler and leader election
|
|
158
170
|
await this._periodicScheduler?.stop();
|
|
159
171
|
await this.leaderElector?.stop();
|
|
160
172
|
this.stopHeartbeat();
|
|
161
173
|
this.stopScheduler();
|
|
174
|
+
// Wait for heartbeat loop to finish
|
|
175
|
+
if (this.heartbeatLoopPromise) {
|
|
176
|
+
await this.heartbeatLoopPromise;
|
|
177
|
+
}
|
|
162
178
|
// Brief pause to allow idle processors to finish
|
|
163
|
-
await
|
|
179
|
+
await sleepWithAbort(PAUSE_TIME_MS, undefined);
|
|
164
180
|
const deadline = Date.now() + this.config.timeout * 1000;
|
|
165
181
|
// Early exit if no jobs are running
|
|
166
182
|
if (this.inProgress.size === 0) {
|
|
@@ -224,6 +240,7 @@ export class Runner {
|
|
|
224
240
|
}
|
|
225
241
|
startScheduler() {
|
|
226
242
|
this.schedulerRunning = true;
|
|
243
|
+
this.schedulerController = new AbortController();
|
|
227
244
|
this.runSchedulerLoop().catch(() => undefined);
|
|
228
245
|
}
|
|
229
246
|
async runSchedulerLoop() {
|
|
@@ -232,7 +249,7 @@ export class Runner {
|
|
|
232
249
|
while (this.schedulerRunning && !this.stopping) {
|
|
233
250
|
await this.enqueueScheduled().catch(() => undefined);
|
|
234
251
|
const intervalMs = await this.randomPollInterval();
|
|
235
|
-
await
|
|
252
|
+
await sleepWithAbort(intervalMs, this.schedulerController?.signal);
|
|
236
253
|
}
|
|
237
254
|
}
|
|
238
255
|
async initialWait() {
|
|
@@ -245,7 +262,7 @@ export class Runner {
|
|
|
245
262
|
}
|
|
246
263
|
waitSeconds += Math.random() * 5; // 0-5 seconds jitter
|
|
247
264
|
if (waitSeconds > 0) {
|
|
248
|
-
await
|
|
265
|
+
await sleepWithAbort(waitSeconds * 1000, this.schedulerController?.signal);
|
|
249
266
|
}
|
|
250
267
|
// Run cleanup after initial wait
|
|
251
268
|
const redis = this.baseRedis ?? (await this.config.getRedisClient());
|
|
@@ -279,10 +296,7 @@ export class Runner {
|
|
|
279
296
|
}
|
|
280
297
|
stopScheduler() {
|
|
281
298
|
this.schedulerRunning = false;
|
|
282
|
-
|
|
283
|
-
clearInterval(this.schedulerHandle);
|
|
284
|
-
this.schedulerHandle = undefined;
|
|
285
|
-
}
|
|
299
|
+
this.schedulerController?.abort();
|
|
286
300
|
}
|
|
287
301
|
async heartbeat() {
|
|
288
302
|
if (this.stopping) {
|
|
@@ -490,44 +504,55 @@ export class Runner {
|
|
|
490
504
|
}
|
|
491
505
|
}
|
|
492
506
|
startHeartbeat() {
|
|
507
|
+
this.heartbeatController = new AbortController();
|
|
508
|
+
this.heartbeatLoopPromise = this.heartbeatLoop();
|
|
509
|
+
}
|
|
510
|
+
async heartbeatLoop() {
|
|
493
511
|
const intervalMs = this.config.heartbeatInterval * 1000;
|
|
494
|
-
this.
|
|
495
|
-
this.
|
|
496
|
-
|
|
512
|
+
while (!this.stopping) {
|
|
513
|
+
await sleepWithAbort(intervalMs, this.heartbeatController?.signal);
|
|
514
|
+
if (this.stopping) {
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
await this.heartbeat().catch(() => undefined);
|
|
518
|
+
}
|
|
497
519
|
}
|
|
498
520
|
stopHeartbeat() {
|
|
499
|
-
|
|
500
|
-
clearInterval(this.heartbeatHandle);
|
|
501
|
-
this.heartbeatHandle = undefined;
|
|
502
|
-
}
|
|
521
|
+
this.heartbeatController?.abort();
|
|
503
522
|
}
|
|
504
523
|
async enqueueScheduled() {
|
|
505
524
|
if (this.stopping) {
|
|
506
525
|
return;
|
|
507
526
|
}
|
|
508
527
|
const redis = this.baseRedis ?? (await this.config.getRedisClient());
|
|
509
|
-
const
|
|
510
|
-
const
|
|
528
|
+
const nowMs = Date.now();
|
|
529
|
+
const nowSeconds = nowMs / 1000;
|
|
511
530
|
const sets = ["schedule", "retry"];
|
|
512
531
|
for (const set of sets) {
|
|
513
532
|
while (!this.stopping) {
|
|
533
|
+
// Atomic operation: pop from sorted set AND push to queue in one step
|
|
534
|
+
// This eliminates data loss if process crashes between operations
|
|
514
535
|
const job = (await redis.sendCommand([
|
|
515
536
|
"EVAL",
|
|
516
|
-
|
|
537
|
+
LUA_RELIABLE_ENQUEUE,
|
|
517
538
|
"1",
|
|
518
539
|
set,
|
|
519
|
-
String(
|
|
540
|
+
String(nowSeconds),
|
|
541
|
+
String(nowMs),
|
|
520
542
|
]));
|
|
521
543
|
if (!job) {
|
|
522
544
|
break;
|
|
523
545
|
}
|
|
524
|
-
|
|
525
|
-
await client.push(payload);
|
|
546
|
+
// Job was atomically moved to its queue - no additional action needed
|
|
526
547
|
}
|
|
527
548
|
}
|
|
528
549
|
}
|
|
529
550
|
async workLoop(index) {
|
|
530
|
-
while (
|
|
551
|
+
while (true) {
|
|
552
|
+
// Check stopping BEFORE fetching - don't start new work
|
|
553
|
+
if (this.stopping) {
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
531
556
|
if (this.quieting) {
|
|
532
557
|
await sleep(50);
|
|
533
558
|
continue;
|
|
@@ -536,6 +561,8 @@ export class Runner {
|
|
|
536
561
|
if (!unit) {
|
|
537
562
|
continue;
|
|
538
563
|
}
|
|
564
|
+
// Process the job - even if stopping became true during fetchWork
|
|
565
|
+
// Once we've popped a job from Redis, we MUST process it
|
|
539
566
|
const workerId = `worker-${index}`;
|
|
540
567
|
this.inProgress.set(workerId, {
|
|
541
568
|
queue: unit.queue,
|
|
@@ -599,7 +626,10 @@ export class Runner {
|
|
|
599
626
|
}
|
|
600
627
|
const job = new klass();
|
|
601
628
|
job.jid = payload.jid;
|
|
602
|
-
job._context = {
|
|
629
|
+
job._context = {
|
|
630
|
+
stopping: () => this.stopping,
|
|
631
|
+
signal: this.jobController?.signal,
|
|
632
|
+
};
|
|
603
633
|
try {
|
|
604
634
|
let executed = false;
|
|
605
635
|
await this.jobLogger.prepare(payload, async () => {
|
package/dist/sidekiq.d.ts
CHANGED
|
@@ -2,7 +2,13 @@ import { Config } from "./config.js";
|
|
|
2
2
|
import type { MiddlewareConstructor } from "./middleware.js";
|
|
3
3
|
import type { RedisClient } from "./redis.js";
|
|
4
4
|
import { type RegisteredJobClass } from "./registry.js";
|
|
5
|
+
import type { Runner } from "./runner.js";
|
|
5
6
|
import type { ConfigOptions, JobOptions, JobPayload, SidekiqEventHandler, SidekiqEventName, StrictArgsMode } from "./types.js";
|
|
7
|
+
interface RunOptions {
|
|
8
|
+
config?: Config;
|
|
9
|
+
/** Auto-register SIGINT/SIGTERM handlers for graceful shutdown */
|
|
10
|
+
signals?: boolean;
|
|
11
|
+
}
|
|
6
12
|
export declare class Sidekiq {
|
|
7
13
|
static readonly NAME = "Sidekiq";
|
|
8
14
|
static readonly LICENSE = "See LICENSE and the LGPL-3.0 for licensing details.";
|
|
@@ -36,8 +42,8 @@ export declare class Sidekiq {
|
|
|
36
42
|
static redis<T>(fn: (client: Awaited<ReturnType<Config["getRedisClient"]>>) => Promise<T>): Promise<T>;
|
|
37
43
|
static registerJob(klass: RegisteredJobClass): void;
|
|
38
44
|
static registeredJobClass(name: string): RegisteredJobClass | undefined;
|
|
39
|
-
static run({ config }?:
|
|
40
|
-
|
|
41
|
-
}): Promise<import("./runner.js").Runner>;
|
|
45
|
+
static run({ config, signals }?: RunOptions): Promise<Runner>;
|
|
46
|
+
private static registerSignalHandlers;
|
|
42
47
|
}
|
|
48
|
+
export {};
|
|
43
49
|
//# sourceMappingURL=sidekiq.d.ts.map
|
package/dist/sidekiq.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidekiq.d.ts","sourceRoot":"","sources":["../src/sidekiq.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sidekiq.d.ts","sourceRoot":"","sources":["../src/sidekiq.ts"],"names":[],"mappings":"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"}
|
package/dist/sidekiq.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createShutdownHandler } from "./cli-helpers.js";
|
|
1
2
|
import { Config } from "./config.js";
|
|
2
3
|
import { dumpJson, loadJson } from "./json.js";
|
|
3
4
|
import { registeredJob, registerJob, } from "./registry.js";
|
|
@@ -176,7 +177,7 @@ export class Sidekiq {
|
|
|
176
177
|
static registeredJobClass(name) {
|
|
177
178
|
return registeredJob(name);
|
|
178
179
|
}
|
|
179
|
-
static async run({ config } = {}) {
|
|
180
|
+
static async run({ config, signals } = {}) {
|
|
180
181
|
if (config) {
|
|
181
182
|
this.config = config;
|
|
182
183
|
}
|
|
@@ -184,6 +185,29 @@ export class Sidekiq {
|
|
|
184
185
|
const { Runner } = await import("./runner.js");
|
|
185
186
|
const runner = new Runner(this.config);
|
|
186
187
|
await runner.start();
|
|
188
|
+
if (signals) {
|
|
189
|
+
this.registerSignalHandlers(runner);
|
|
190
|
+
}
|
|
187
191
|
return runner;
|
|
188
192
|
}
|
|
193
|
+
static registerSignalHandlers(runner) {
|
|
194
|
+
const shutdown = createShutdownHandler({
|
|
195
|
+
logger: this.config.logger,
|
|
196
|
+
stop: () => runner.stop(),
|
|
197
|
+
exit: (code) => process.exit(code),
|
|
198
|
+
});
|
|
199
|
+
const registerSignal = (signal, handler) => {
|
|
200
|
+
try {
|
|
201
|
+
process.on(signal, () => {
|
|
202
|
+
Promise.resolve(handler()).catch(() => undefined);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// Signal not supported on this platform
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
registerSignal("SIGINT", () => shutdown("SIGINT"));
|
|
210
|
+
registerSignal("SIGTERM", () => shutdown("SIGTERM"));
|
|
211
|
+
registerSignal("SIGTSTP", () => runner.quiet());
|
|
212
|
+
}
|
|
189
213
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -52,6 +52,8 @@ export interface ConfigOptions {
|
|
|
52
52
|
redisIdleTimeout?: number | null;
|
|
53
53
|
/** Leader election configuration */
|
|
54
54
|
leaderElection?: LeaderElectionConfig;
|
|
55
|
+
/** Maximum jobs to hold locally when Redis is unavailable (default: 1000, 0 to disable) */
|
|
56
|
+
reliableClientMaxQueue?: number;
|
|
55
57
|
}
|
|
56
58
|
export type JobRetryOption = boolean | number;
|
|
57
59
|
export interface JobOptions {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|