bullmq 5.80.4 → 5.80.6
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/cjs/classes/child-pool.js +17 -1
- package/dist/cjs/classes/child-processor.js +17 -4
- package/dist/cjs/classes/child.js +8 -2
- package/dist/cjs/classes/job.js +17 -1
- package/dist/cjs/classes/queue.js +11 -2
- package/dist/cjs/classes/sandbox.js +8 -5
- package/dist/cjs/tsconfig-cjs.tsbuildinfo +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/classes/child-pool.js +17 -1
- package/dist/esm/classes/child-processor.js +17 -4
- package/dist/esm/classes/child.js +8 -2
- package/dist/esm/classes/job.d.ts +29 -1
- package/dist/esm/classes/job.js +17 -1
- package/dist/esm/classes/queue.d.ts +11 -2
- package/dist/esm/classes/queue.js +11 -2
- package/dist/esm/classes/sandbox.js +8 -5
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +1 -1
|
@@ -46,7 +46,23 @@ class ChildPool {
|
|
|
46
46
|
}
|
|
47
47
|
catch (err) {
|
|
48
48
|
console.error(err);
|
|
49
|
-
|
|
49
|
+
// A child that failed to initialize (or exited during init) must never
|
|
50
|
+
// be released back into the free pool, otherwise it becomes a "zombie"
|
|
51
|
+
// that is reused for every subsequent job and fails them instantly.
|
|
52
|
+
// Kill and remove it so a fresh child is forked on the next retain.
|
|
53
|
+
// The child also exits itself after a failed init (see ChildProcessor),
|
|
54
|
+
// so this is normally a no-op; log any kill failure instead of silently
|
|
55
|
+
// swallowing it so a lingering child would not go unnoticed.
|
|
56
|
+
if (child.childProcess || child.worker) {
|
|
57
|
+
try {
|
|
58
|
+
this.kill(child, 'SIGKILL').catch(killErr => {
|
|
59
|
+
console.error('Failed to kill child after init error:', killErr);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (killErr) {
|
|
63
|
+
console.error('Failed to kill child after init error:', killErr);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
50
66
|
throw err;
|
|
51
67
|
}
|
|
52
68
|
}
|
|
@@ -25,6 +25,7 @@ class ChildProcessor {
|
|
|
25
25
|
this.receiver = receiver;
|
|
26
26
|
}
|
|
27
27
|
async init(processorFile) {
|
|
28
|
+
var _a;
|
|
28
29
|
let processor;
|
|
29
30
|
try {
|
|
30
31
|
const { default: processorFn } = await import(processorFile);
|
|
@@ -39,10 +40,22 @@ class ChildProcessor {
|
|
|
39
40
|
}
|
|
40
41
|
catch (err) {
|
|
41
42
|
this.status = ChildStatus.Errored;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
try {
|
|
44
|
+
await this.send({
|
|
45
|
+
cmd: enums_1.ParentCommand.InitFailed,
|
|
46
|
+
err: (0, utils_1.errorToJSON)(err),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
// A child that failed to initialize cannot recover, and because the open
|
|
51
|
+
// IPC channel keeps its event loop alive it would never exit on its own.
|
|
52
|
+
// Exit explicitly (after attempting to send InitFailed) so the parent
|
|
53
|
+
// can never reuse a half-initialized "zombie" child. This is a
|
|
54
|
+
// belt-and-braces measure: ChildPool also kills the child, but exiting
|
|
55
|
+
// here guarantees termination even if the parent-side kill were to fail.
|
|
56
|
+
// In a worker thread this stops only the current worker, not the process.
|
|
57
|
+
process.exit((_a = process.exitCode) !== null && _a !== void 0 ? _a : 1);
|
|
58
|
+
}
|
|
46
59
|
}
|
|
47
60
|
const origProcessor = processor;
|
|
48
61
|
processor = function (job, token, signal) {
|
|
@@ -68,6 +68,7 @@ class Child extends events_1.EventEmitter {
|
|
|
68
68
|
return this._killed;
|
|
69
69
|
}
|
|
70
70
|
async init() {
|
|
71
|
+
var _a, _b;
|
|
71
72
|
const execArgv = await convertExecArgv(process.execArgv);
|
|
72
73
|
let parent;
|
|
73
74
|
if (this.opts.useWorkerThreads) {
|
|
@@ -92,8 +93,13 @@ class Child extends events_1.EventEmitter {
|
|
|
92
93
|
parent.on('error', (...args) => this.emit('error', ...args));
|
|
93
94
|
parent.on('message', (...args) => this.emit('message', ...args));
|
|
94
95
|
parent.on('close', (...args) => this.emit('close', ...args));
|
|
95
|
-
parent.stdout.
|
|
96
|
-
|
|
96
|
+
// `parent.stdout`/`parent.stderr` may be null when the underlying runtime
|
|
97
|
+
// does not pipe child stdio (e.g. Bun ignores `worker_threads` stdout/stderr
|
|
98
|
+
// options, and Node returns null when `stdio: 'ignore'` is passed in
|
|
99
|
+
// `workerForkOptions`). Guard the pipe calls so initialization does not throw.
|
|
100
|
+
// See https://github.com/taskforcesh/bullmq/issues/2232
|
|
101
|
+
(_a = parent.stdout) === null || _a === void 0 ? void 0 : _a.pipe(process.stdout);
|
|
102
|
+
(_b = parent.stderr) === null || _b === void 0 ? void 0 : _b.pipe(process.stderr);
|
|
97
103
|
await this.initChild();
|
|
98
104
|
}
|
|
99
105
|
async send(msg) {
|
package/dist/cjs/classes/job.js
CHANGED
|
@@ -27,6 +27,18 @@ class Job {
|
|
|
27
27
|
name,
|
|
28
28
|
/**
|
|
29
29
|
* The payload for this job.
|
|
30
|
+
*
|
|
31
|
+
* Note: The `DataType` type parameter describes the *shape* of the
|
|
32
|
+
* payload, not its runtime class. Job data is serialized with
|
|
33
|
+
* `JSON.stringify` before being stored in Redis and deserialized with
|
|
34
|
+
* `JSON.parse` when read back, so when a worker picks up the job
|
|
35
|
+
* `job.data` will contain a JSON-compatible value such as an object,
|
|
36
|
+
* array, string, number, boolean, or `null`. Class instances passed as
|
|
37
|
+
* `data` lose their prototype (and therefore any methods,
|
|
38
|
+
* getters/setters, or non-enumerable properties) on the worker side.
|
|
39
|
+
* If you need methods, store the data as a plain object and
|
|
40
|
+
* re-instantiate the class inside your processor, or rely on
|
|
41
|
+
* `JSON.stringify`'s built-in `toJSON()` hook to control serialization.
|
|
30
42
|
*/
|
|
31
43
|
data,
|
|
32
44
|
/**
|
|
@@ -120,7 +132,11 @@ class Job {
|
|
|
120
132
|
*
|
|
121
133
|
* @param queue - the queue where to add the job.
|
|
122
134
|
* @param name - the name of the job.
|
|
123
|
-
* @param data - the payload of the job.
|
|
135
|
+
* @param data - the payload of the job. It will be serialized with
|
|
136
|
+
* `JSON.stringify` before being stored in Redis, so it must be
|
|
137
|
+
* JSON-serializable. Class instances are flattened to plain objects and
|
|
138
|
+
* lose their prototype methods on the worker side; see the
|
|
139
|
+
* {@link Job} class for details.
|
|
124
140
|
* @param opts - the options bag for this job.
|
|
125
141
|
* @returns The created Job instance
|
|
126
142
|
*/
|
|
@@ -145,7 +145,14 @@ class Queue extends queue_getters_1.QueueGetters {
|
|
|
145
145
|
* Adds a new job to the queue.
|
|
146
146
|
*
|
|
147
147
|
* @param name - Name of the job to be added to the queue.
|
|
148
|
-
* @param data - Arbitrary data to append to the job.
|
|
148
|
+
* @param data - Arbitrary data to append to the job. The value is
|
|
149
|
+
* serialized with `JSON.stringify` before being stored in Redis, so it
|
|
150
|
+
* must be JSON-serializable. The `DataType` type parameter describes the
|
|
151
|
+
* shape of this payload, not a runtime class: passing a class instance
|
|
152
|
+
* will store its enumerable own properties, but its prototype methods
|
|
153
|
+
* and getters/setters will not be available when a worker reads
|
|
154
|
+
* `job.data` back. Prefer plain objects, or implement `toJSON()` on
|
|
155
|
+
* the class to control how it is serialized.
|
|
149
156
|
* @param opts - Job options that affects how the job is going to be processed.
|
|
150
157
|
*/
|
|
151
158
|
async add(name, data, opts) {
|
|
@@ -200,7 +207,9 @@ class Queue extends queue_getters_1.QueueGetters {
|
|
|
200
207
|
* one job at a time in a sequence.
|
|
201
208
|
*
|
|
202
209
|
* @param jobs - The array of jobs to add to the queue. Each job is defined by 3
|
|
203
|
-
* properties, 'name', 'data' and 'opts'. They follow the same signature as 'Queue.add'
|
|
210
|
+
* properties, 'name', 'data' and 'opts'. They follow the same signature as 'Queue.add',
|
|
211
|
+
* including the JSON-serialization caveat for `data` (class instances lose their
|
|
212
|
+
* prototype methods on the worker side).
|
|
204
213
|
*/
|
|
205
214
|
async addBulk(jobs) {
|
|
206
215
|
return this.trace(enums_1.SpanKind.PRODUCER, 'addBulk', this.name, async (span, srcPropagationMetadata) => {
|
|
@@ -17,7 +17,7 @@ const sandbox = (processFile, childPool) => {
|
|
|
17
17
|
child = await childPool.retain(processFile);
|
|
18
18
|
child.on('exit', exitHandler);
|
|
19
19
|
msgHandler = async (msg) => {
|
|
20
|
-
var _a, _b, _c, _d, _e;
|
|
20
|
+
var _a, _b, _c, _d, _e, _f;
|
|
21
21
|
try {
|
|
22
22
|
switch (msg.cmd) {
|
|
23
23
|
case enums_1.ParentCommand.Completed:
|
|
@@ -26,7 +26,10 @@ const sandbox = (processFile, childPool) => {
|
|
|
26
26
|
case enums_1.ParentCommand.Failed:
|
|
27
27
|
case enums_1.ParentCommand.Error: {
|
|
28
28
|
const err = new Error();
|
|
29
|
-
|
|
29
|
+
// ParentCommand.Failed carries the error under `value`,
|
|
30
|
+
// while ParentCommand.Error carries it under `err`. Read
|
|
31
|
+
// from either key so the failure reason is never lost.
|
|
32
|
+
Object.assign(err, (_a = msg.value) !== null && _a !== void 0 ? _a : msg.err);
|
|
30
33
|
reject(err);
|
|
31
34
|
break;
|
|
32
35
|
}
|
|
@@ -37,14 +40,14 @@ const sandbox = (processFile, childPool) => {
|
|
|
37
40
|
await job.log(msg.value);
|
|
38
41
|
break;
|
|
39
42
|
case enums_1.ParentCommand.MoveToDelayed:
|
|
40
|
-
await job.moveToDelayed((
|
|
43
|
+
await job.moveToDelayed((_b = msg.value) === null || _b === void 0 ? void 0 : _b.timestamp, (_c = msg.value) === null || _c === void 0 ? void 0 : _c.token);
|
|
41
44
|
break;
|
|
42
45
|
case enums_1.ParentCommand.MoveToWait:
|
|
43
|
-
await job.moveToWait((
|
|
46
|
+
await job.moveToWait((_d = msg.value) === null || _d === void 0 ? void 0 : _d.token);
|
|
44
47
|
break;
|
|
45
48
|
case enums_1.ParentCommand.MoveToWaitingChildren:
|
|
46
49
|
{
|
|
47
|
-
const value = await job.moveToWaitingChildren((
|
|
50
|
+
const value = await job.moveToWaitingChildren((_e = msg.value) === null || _e === void 0 ? void 0 : _e.token, (_f = msg.value) === null || _f === void 0 ? void 0 : _f.opts);
|
|
48
51
|
child.send({
|
|
49
52
|
requestId: msg.requestId,
|
|
50
53
|
cmd: enums_1.ChildCommand.MoveToWaitingChildrenResponse,
|