@pauldeng/node-red-contrib-bullmq 1.0.0
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/LICENSE +22 -0
- package/README.md +124 -0
- package/bull-queue.html +530 -0
- package/bull-queue.js +523 -0
- package/docs/ARCHITECTURE.md +52 -0
- package/docs/CHANGE_WORKFLOW.md +27 -0
- package/docs/COMMANDS.md +89 -0
- package/docs/CONNECTIONS.md +54 -0
- package/docs/MIGRATION.md +37 -0
- package/docs/NODE_GUIDE.md +83 -0
- package/docs/REFERENCE_MAP.md +51 -0
- package/docs/RELEASE.md +108 -0
- package/docs/TESTING.md +88 -0
- package/docs/TROUBLESHOOTING.md +28 -0
- package/examples/README.md +35 -0
- package/examples/bullmq_features.json +331 -0
- package/examples/example_flow.json +277 -0
- package/examples/repeatable_jobs.json +245 -0
- package/icons/bull_icon.png +0 -0
- package/lib/acknowledgements.js +110 -0
- package/lib/commands.js +230 -0
- package/lib/connections.js +300 -0
- package/lib/scheduler.js +114 -0
- package/lib/serialization.js +63 -0
- package/package.json +70 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "flow-repeatable-jobs",
|
|
4
|
+
"type": "tab",
|
|
5
|
+
"label": "BullMQ repeatable jobs",
|
|
6
|
+
"disabled": false,
|
|
7
|
+
"info": "Dedicated repeatable job command examples for BullMQ Job Scheduler compatibility commands."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": "queue-repeatable-jobs",
|
|
11
|
+
"type": "bull-queue-server",
|
|
12
|
+
"name": "basecasts",
|
|
13
|
+
"deployment": "single",
|
|
14
|
+
"address": "localhost",
|
|
15
|
+
"port": "6379",
|
|
16
|
+
"db": "",
|
|
17
|
+
"clusterNodes": "",
|
|
18
|
+
"sentinels": "",
|
|
19
|
+
"sentinelMasterName": "",
|
|
20
|
+
"username": "",
|
|
21
|
+
"sentinelUsername": "",
|
|
22
|
+
"tls": false,
|
|
23
|
+
"sentinelTls": false,
|
|
24
|
+
"tlsRejectUnauthorized": true,
|
|
25
|
+
"tlsServerName": "",
|
|
26
|
+
"prefix": ""
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "inject-repeat-add",
|
|
30
|
+
"type": "inject",
|
|
31
|
+
"z": "flow-repeatable-jobs",
|
|
32
|
+
"name": "repeat: add basecasts job",
|
|
33
|
+
"props": [{ "p": "payload" }],
|
|
34
|
+
"repeat": "",
|
|
35
|
+
"crontab": "",
|
|
36
|
+
"once": false,
|
|
37
|
+
"onceDelay": 0.1,
|
|
38
|
+
"topic": "",
|
|
39
|
+
"payload": "gateway-FCC23DFFFE0AA2A8",
|
|
40
|
+
"payloadType": "str",
|
|
41
|
+
"x": 180,
|
|
42
|
+
"y": 100,
|
|
43
|
+
"wires": [["fn-repeat-add"]]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "fn-repeat-add",
|
|
47
|
+
"type": "function",
|
|
48
|
+
"z": "flow-repeatable-jobs",
|
|
49
|
+
"name": "add repeat cron",
|
|
50
|
+
"func": "msg.cmd = \"add\";\nmsg.jobopts = {\"jobId\": msg.payload, \"repeat\": {\"cron\": \"30 9,19,29,39,49,59 * * * *\"}};\nreturn msg;",
|
|
51
|
+
"outputs": 1,
|
|
52
|
+
"noerr": 0,
|
|
53
|
+
"initialize": "",
|
|
54
|
+
"finalize": "",
|
|
55
|
+
"libs": [],
|
|
56
|
+
"x": 430,
|
|
57
|
+
"y": 100,
|
|
58
|
+
"wires": [["cmd-repeatable-jobs"]]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "inject-repeat-get-all",
|
|
62
|
+
"type": "inject",
|
|
63
|
+
"z": "flow-repeatable-jobs",
|
|
64
|
+
"name": "repeat: getRepeatableJobs",
|
|
65
|
+
"props": [{ "p": "payload" }],
|
|
66
|
+
"repeat": "",
|
|
67
|
+
"crontab": "",
|
|
68
|
+
"once": false,
|
|
69
|
+
"onceDelay": 0.1,
|
|
70
|
+
"topic": "",
|
|
71
|
+
"payload": "",
|
|
72
|
+
"payloadType": "str",
|
|
73
|
+
"x": 190,
|
|
74
|
+
"y": 180,
|
|
75
|
+
"wires": [["fn-repeat-get-all"]]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": "fn-repeat-get-all",
|
|
79
|
+
"type": "function",
|
|
80
|
+
"z": "flow-repeatable-jobs",
|
|
81
|
+
"name": "getRepeatableJobs",
|
|
82
|
+
"func": "msg.cmd = \"getRepeatableJobs\";\nreturn msg;",
|
|
83
|
+
"outputs": 1,
|
|
84
|
+
"noerr": 0,
|
|
85
|
+
"initialize": "",
|
|
86
|
+
"finalize": "",
|
|
87
|
+
"libs": [],
|
|
88
|
+
"x": 440,
|
|
89
|
+
"y": 180,
|
|
90
|
+
"wires": [["cmd-repeatable-jobs"]]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "inject-repeat-count",
|
|
94
|
+
"type": "inject",
|
|
95
|
+
"z": "flow-repeatable-jobs",
|
|
96
|
+
"name": "repeat: count",
|
|
97
|
+
"props": [{ "p": "payload" }],
|
|
98
|
+
"repeat": "",
|
|
99
|
+
"crontab": "",
|
|
100
|
+
"once": false,
|
|
101
|
+
"onceDelay": 0.1,
|
|
102
|
+
"topic": "",
|
|
103
|
+
"payload": "",
|
|
104
|
+
"payloadType": "str",
|
|
105
|
+
"x": 150,
|
|
106
|
+
"y": 260,
|
|
107
|
+
"wires": [["fn-repeat-count"]]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"id": "fn-repeat-count",
|
|
111
|
+
"type": "function",
|
|
112
|
+
"z": "flow-repeatable-jobs",
|
|
113
|
+
"name": "count",
|
|
114
|
+
"func": "msg.cmd = \"count\";\nreturn msg;",
|
|
115
|
+
"outputs": 1,
|
|
116
|
+
"noerr": 0,
|
|
117
|
+
"initialize": "",
|
|
118
|
+
"finalize": "",
|
|
119
|
+
"libs": [],
|
|
120
|
+
"x": 410,
|
|
121
|
+
"y": 260,
|
|
122
|
+
"wires": [["cmd-repeatable-jobs"]]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"id": "inject-repeat-get-by-key",
|
|
126
|
+
"type": "inject",
|
|
127
|
+
"z": "flow-repeatable-jobs",
|
|
128
|
+
"name": "repeat: getRepeatableJobByKey",
|
|
129
|
+
"props": [{ "p": "payload" }],
|
|
130
|
+
"repeat": "",
|
|
131
|
+
"crontab": "",
|
|
132
|
+
"once": false,
|
|
133
|
+
"onceDelay": 0.1,
|
|
134
|
+
"topic": "",
|
|
135
|
+
"payload": "gateway-FCC23DFFFE0AA2A8",
|
|
136
|
+
"payloadType": "str",
|
|
137
|
+
"x": 220,
|
|
138
|
+
"y": 340,
|
|
139
|
+
"wires": [["fn-repeat-get-by-key"]]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"id": "fn-repeat-get-by-key",
|
|
143
|
+
"type": "function",
|
|
144
|
+
"z": "flow-repeatable-jobs",
|
|
145
|
+
"name": "getRepeatableJobByKey",
|
|
146
|
+
"func": "msg.cmd = \"getRepeatableJobByKey\";\nmsg.jobid = msg.payload;\nreturn msg;",
|
|
147
|
+
"outputs": 1,
|
|
148
|
+
"noerr": 0,
|
|
149
|
+
"initialize": "",
|
|
150
|
+
"finalize": "",
|
|
151
|
+
"libs": [],
|
|
152
|
+
"x": 470,
|
|
153
|
+
"y": 340,
|
|
154
|
+
"wires": [["cmd-repeatable-jobs"]]
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"id": "inject-repeat-remove-by-key",
|
|
158
|
+
"type": "inject",
|
|
159
|
+
"z": "flow-repeatable-jobs",
|
|
160
|
+
"name": "repeat: removeRepeatableByKey",
|
|
161
|
+
"props": [{ "p": "payload" }],
|
|
162
|
+
"repeat": "",
|
|
163
|
+
"crontab": "",
|
|
164
|
+
"once": false,
|
|
165
|
+
"onceDelay": 0.1,
|
|
166
|
+
"topic": "",
|
|
167
|
+
"payload": "gateway-FCC23DFFFE0AA2A8",
|
|
168
|
+
"payloadType": "str",
|
|
169
|
+
"x": 230,
|
|
170
|
+
"y": 420,
|
|
171
|
+
"wires": [["fn-repeat-remove-by-key"]]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"id": "fn-repeat-remove-by-key",
|
|
175
|
+
"type": "function",
|
|
176
|
+
"z": "flow-repeatable-jobs",
|
|
177
|
+
"name": "removeRepeatableByKey",
|
|
178
|
+
"func": "msg.cmd = \"removeRepeatableByKey\";\nmsg.jobid = msg.payload;\nreturn msg;",
|
|
179
|
+
"outputs": 1,
|
|
180
|
+
"noerr": 0,
|
|
181
|
+
"initialize": "",
|
|
182
|
+
"finalize": "",
|
|
183
|
+
"libs": [],
|
|
184
|
+
"x": 480,
|
|
185
|
+
"y": 420,
|
|
186
|
+
"wires": [["cmd-repeatable-jobs"]]
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"id": "inject-repeat-stop-all",
|
|
190
|
+
"type": "inject",
|
|
191
|
+
"z": "flow-repeatable-jobs",
|
|
192
|
+
"name": "repeat: stopAndRemoveAllJobs",
|
|
193
|
+
"props": [{ "p": "payload" }],
|
|
194
|
+
"repeat": "",
|
|
195
|
+
"crontab": "",
|
|
196
|
+
"once": false,
|
|
197
|
+
"onceDelay": 0.1,
|
|
198
|
+
"topic": "",
|
|
199
|
+
"payload": "",
|
|
200
|
+
"payloadType": "str",
|
|
201
|
+
"x": 210,
|
|
202
|
+
"y": 500,
|
|
203
|
+
"wires": [["fn-repeat-stop-all"]]
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"id": "fn-repeat-stop-all",
|
|
207
|
+
"type": "function",
|
|
208
|
+
"z": "flow-repeatable-jobs",
|
|
209
|
+
"name": "stopAndRemoveAllJobs",
|
|
210
|
+
"func": "msg.cmd = \"stopAndRemoveAllJobs\";\nreturn msg;",
|
|
211
|
+
"outputs": 1,
|
|
212
|
+
"noerr": 0,
|
|
213
|
+
"initialize": "",
|
|
214
|
+
"finalize": "",
|
|
215
|
+
"libs": [],
|
|
216
|
+
"x": 470,
|
|
217
|
+
"y": 500,
|
|
218
|
+
"wires": [["cmd-repeatable-jobs"]]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"id": "cmd-repeatable-jobs",
|
|
222
|
+
"type": "bull cmd",
|
|
223
|
+
"z": "flow-repeatable-jobs",
|
|
224
|
+
"name": "run repeat command",
|
|
225
|
+
"queue": "queue-repeatable-jobs",
|
|
226
|
+
"x": 740,
|
|
227
|
+
"y": 260,
|
|
228
|
+
"wires": [["debug-repeatable-jobs"]]
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"id": "debug-repeatable-jobs",
|
|
232
|
+
"type": "debug",
|
|
233
|
+
"z": "flow-repeatable-jobs",
|
|
234
|
+
"name": "repeat command output",
|
|
235
|
+
"active": true,
|
|
236
|
+
"tosidebar": true,
|
|
237
|
+
"console": false,
|
|
238
|
+
"tostatus": false,
|
|
239
|
+
"complete": "true",
|
|
240
|
+
"targetType": "full",
|
|
241
|
+
"x": 1000,
|
|
242
|
+
"y": 260,
|
|
243
|
+
"wires": []
|
|
244
|
+
}
|
|
245
|
+
]
|
|
Binary file
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { EventEmitter, once } = require("node:events");
|
|
4
|
+
|
|
5
|
+
class AcknowledgementEntry {
|
|
6
|
+
constructor(context, timeoutMs) {
|
|
7
|
+
this.events = new EventEmitter();
|
|
8
|
+
this.settled = false;
|
|
9
|
+
this.job = context.job;
|
|
10
|
+
this.queue = context.queue;
|
|
11
|
+
this.queueName = context.queueName;
|
|
12
|
+
this.runNodeId = context.runNodeId;
|
|
13
|
+
this.timeout = undefined;
|
|
14
|
+
|
|
15
|
+
if (timeoutMs > 0) {
|
|
16
|
+
this.timeout = setTimeout(() => {
|
|
17
|
+
this.fail(
|
|
18
|
+
new Error(
|
|
19
|
+
`BullMQ job acknowledgement timed out after ${timeoutMs}ms`,
|
|
20
|
+
),
|
|
21
|
+
);
|
|
22
|
+
}, timeoutMs);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async wait() {
|
|
27
|
+
const [settlement] = await once(this.events, "settled");
|
|
28
|
+
if (settlement.type === "reject") {
|
|
29
|
+
throw settlement.error;
|
|
30
|
+
}
|
|
31
|
+
return settlement.value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
complete(value) {
|
|
35
|
+
this.settle({ type: "resolve", value });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fail(error) {
|
|
39
|
+
this.settle({ type: "reject", error });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
settle(settlement) {
|
|
43
|
+
if (this.settled) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
this.settled = true;
|
|
47
|
+
clearTimeout(this.timeout);
|
|
48
|
+
this.events.emit("settled", settlement);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Parse a manual-acknowledgement timeout. Empty, non-numeric, or negative
|
|
53
|
+
// values fall back to defaultMs; 0 disables the timeout (wait indefinitely).
|
|
54
|
+
function parseAckTimeoutMs(value, defaultMs = 300000) {
|
|
55
|
+
if (value === undefined || value === null || value === "") {
|
|
56
|
+
return defaultMs;
|
|
57
|
+
}
|
|
58
|
+
const parsed = Number(value);
|
|
59
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
60
|
+
return defaultMs;
|
|
61
|
+
}
|
|
62
|
+
return parsed;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
class AcknowledgementRegistry {
|
|
66
|
+
constructor() {
|
|
67
|
+
this.entries = new Map();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
create(context, timeoutMs) {
|
|
71
|
+
const ackId = `${context.runNodeId}:${context.job.id}:${Date.now()}:${Math.random()
|
|
72
|
+
.toString(36)
|
|
73
|
+
.slice(2)}`;
|
|
74
|
+
|
|
75
|
+
const entry = new AcknowledgementEntry(context, timeoutMs);
|
|
76
|
+
this.entries.set(ackId, entry);
|
|
77
|
+
// Drop the entry as soon as it settles (complete, fail, timeout, or
|
|
78
|
+
// rejectByRunNode) so the registry never accumulates finished jobs.
|
|
79
|
+
entry.events.once("settled", () => {
|
|
80
|
+
this.entries.delete(ackId);
|
|
81
|
+
});
|
|
82
|
+
return { ackId, entry };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get(ackId) {
|
|
86
|
+
const entry = this.entries.get(ackId);
|
|
87
|
+
if (!entry) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
"Missing, stale, or already-settled BullMQ acknowledgement",
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
return entry;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
rejectByRunNode(runNodeId, err) {
|
|
96
|
+
// Snapshot first: failing an entry settles it, which removes it from the
|
|
97
|
+
// map via the "settled" handler registered in create().
|
|
98
|
+
for (const entry of Array.from(this.entries.values())) {
|
|
99
|
+
if (entry.runNodeId === runNodeId) {
|
|
100
|
+
entry.fail(err);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
AcknowledgementEntry,
|
|
108
|
+
AcknowledgementRegistry,
|
|
109
|
+
parseAckTimeoutMs,
|
|
110
|
+
};
|
package/lib/commands.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getLegacySchedulerId,
|
|
5
|
+
normalizeAddRequest,
|
|
6
|
+
serializeScheduler,
|
|
7
|
+
} = require("./scheduler");
|
|
8
|
+
const { serializeJob } = require("./serialization");
|
|
9
|
+
|
|
10
|
+
const CLEAN_STATES = ["completed", "failed", "delayed", "wait"];
|
|
11
|
+
|
|
12
|
+
function valueOrDefault(value, defaultValue) {
|
|
13
|
+
return value === undefined || value === null ? defaultValue : value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function getRequiredJob(queue, msg) {
|
|
17
|
+
const jobId = String(msg.jobId || msg.jobid || "").trim();
|
|
18
|
+
if (!jobId) {
|
|
19
|
+
throw new Error("msg.jobId is required");
|
|
20
|
+
}
|
|
21
|
+
const job = await queue.getJob(jobId);
|
|
22
|
+
if (!job) {
|
|
23
|
+
throw new Error(`Job not found: ${jobId}`);
|
|
24
|
+
}
|
|
25
|
+
return job;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function dispatchAdd(queue, msg) {
|
|
29
|
+
const request = normalizeAddRequest(msg);
|
|
30
|
+
if (request.kind === "scheduler") {
|
|
31
|
+
return serializeJob(
|
|
32
|
+
await queue.upsertJobScheduler(
|
|
33
|
+
request.schedulerId,
|
|
34
|
+
request.repeat,
|
|
35
|
+
request.template,
|
|
36
|
+
),
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return serializeJob(
|
|
40
|
+
await queue.add(request.name, request.data, request.opts),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function dispatchStopAndRemoveAllJobs(queue) {
|
|
45
|
+
const schedulers = await queue.getJobSchedulers(0, -1, true);
|
|
46
|
+
const removedSchedulers = [];
|
|
47
|
+
for (const scheduler of schedulers || []) {
|
|
48
|
+
const id = scheduler.id || scheduler.key;
|
|
49
|
+
if (id) {
|
|
50
|
+
await queue.removeJobScheduler(id);
|
|
51
|
+
removedSchedulers.push(id);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await queue.drain(true);
|
|
56
|
+
|
|
57
|
+
const cleaned = {};
|
|
58
|
+
for (const state of CLEAN_STATES) {
|
|
59
|
+
cleaned[state] = await queue.clean(0, 1000, state);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { removedSchedulers, cleaned };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function dispatchCommand(queue, msg = {}) {
|
|
66
|
+
const cmd = msg.cmd || msg.command || "add";
|
|
67
|
+
|
|
68
|
+
switch (cmd) {
|
|
69
|
+
case "add":
|
|
70
|
+
return dispatchAdd(queue, msg);
|
|
71
|
+
case "addBulk":
|
|
72
|
+
return (await queue.addBulk(msg.payload || [])).map(serializeJob);
|
|
73
|
+
case "getJob":
|
|
74
|
+
return serializeJob(await queue.getJob(msg.jobId || msg.jobid));
|
|
75
|
+
case "getJobs":
|
|
76
|
+
return (
|
|
77
|
+
await queue.getJobs(
|
|
78
|
+
msg.types,
|
|
79
|
+
valueOrDefault(msg.start, 0),
|
|
80
|
+
valueOrDefault(msg.end, -1),
|
|
81
|
+
valueOrDefault(msg.asc, false),
|
|
82
|
+
)
|
|
83
|
+
).map(serializeJob);
|
|
84
|
+
case "getJobState": {
|
|
85
|
+
const job = await getRequiredJob(queue, msg);
|
|
86
|
+
return job.getState();
|
|
87
|
+
}
|
|
88
|
+
case "removeJob": {
|
|
89
|
+
const job = await getRequiredJob(queue, msg);
|
|
90
|
+
await job.remove();
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
case "retryJob": {
|
|
94
|
+
const job = await getRequiredJob(queue, msg);
|
|
95
|
+
await job.retry(msg.state);
|
|
96
|
+
return serializeJob(job);
|
|
97
|
+
}
|
|
98
|
+
case "retryJobs":
|
|
99
|
+
return queue.retryJobs(msg.opts || {});
|
|
100
|
+
case "getDelayed":
|
|
101
|
+
return (
|
|
102
|
+
await queue.getDelayed(
|
|
103
|
+
valueOrDefault(msg.start, 0),
|
|
104
|
+
valueOrDefault(msg.end, -1),
|
|
105
|
+
valueOrDefault(msg.asc, false),
|
|
106
|
+
)
|
|
107
|
+
).map(serializeJob);
|
|
108
|
+
case "changeDelay": {
|
|
109
|
+
const job = await getRequiredJob(queue, msg);
|
|
110
|
+
await job.changeDelay(msg.delay);
|
|
111
|
+
return serializeJob(job);
|
|
112
|
+
}
|
|
113
|
+
case "promoteJob": {
|
|
114
|
+
const job = await getRequiredJob(queue, msg);
|
|
115
|
+
await job.promote();
|
|
116
|
+
return serializeJob(job);
|
|
117
|
+
}
|
|
118
|
+
case "promoteJobs":
|
|
119
|
+
return queue.promoteJobs();
|
|
120
|
+
case "getPrioritized":
|
|
121
|
+
return (
|
|
122
|
+
await queue.getPrioritized(
|
|
123
|
+
valueOrDefault(msg.start, 0),
|
|
124
|
+
valueOrDefault(msg.end, -1),
|
|
125
|
+
)
|
|
126
|
+
).map(serializeJob);
|
|
127
|
+
case "getCountsPerPriority":
|
|
128
|
+
return queue.getCountsPerPriority(msg.priorities || []);
|
|
129
|
+
case "changePriority": {
|
|
130
|
+
const job = await getRequiredJob(queue, msg);
|
|
131
|
+
await job.changePriority({
|
|
132
|
+
priority: msg.priority,
|
|
133
|
+
lifo: valueOrDefault(msg.lifo, false),
|
|
134
|
+
});
|
|
135
|
+
return serializeJob(job);
|
|
136
|
+
}
|
|
137
|
+
case "getDeduplicationJobId":
|
|
138
|
+
return queue.getDeduplicationJobId(msg.deduplicationId);
|
|
139
|
+
case "removeDeduplicationKey":
|
|
140
|
+
return queue.removeDeduplicationKey(msg.deduplicationId);
|
|
141
|
+
case "upsertJobScheduler":
|
|
142
|
+
return serializeJob(
|
|
143
|
+
await queue.upsertJobScheduler(
|
|
144
|
+
getLegacySchedulerId(msg),
|
|
145
|
+
msg.repeat,
|
|
146
|
+
msg.template,
|
|
147
|
+
),
|
|
148
|
+
);
|
|
149
|
+
case "getJobScheduler":
|
|
150
|
+
return serializeScheduler(
|
|
151
|
+
await queue.getJobScheduler(getLegacySchedulerId(msg)),
|
|
152
|
+
);
|
|
153
|
+
case "getJobSchedulers":
|
|
154
|
+
case "getRepeatableJobs":
|
|
155
|
+
return (
|
|
156
|
+
await queue.getJobSchedulers(
|
|
157
|
+
valueOrDefault(msg.start, 0),
|
|
158
|
+
valueOrDefault(msg.end, -1),
|
|
159
|
+
valueOrDefault(msg.asc, true),
|
|
160
|
+
)
|
|
161
|
+
).map(serializeScheduler);
|
|
162
|
+
case "getJobSchedulersCount":
|
|
163
|
+
case "count":
|
|
164
|
+
return queue.getJobSchedulersCount();
|
|
165
|
+
case "removeJobScheduler":
|
|
166
|
+
case "removeRepeatableByKey":
|
|
167
|
+
return queue.removeJobScheduler(getLegacySchedulerId(msg));
|
|
168
|
+
case "getRepeatableJobByKey":
|
|
169
|
+
return serializeScheduler(
|
|
170
|
+
await queue.getJobScheduler(getLegacySchedulerId(msg)),
|
|
171
|
+
);
|
|
172
|
+
case "getJobCounts":
|
|
173
|
+
return queue.getJobCounts(...(msg.types || []));
|
|
174
|
+
case "pause":
|
|
175
|
+
await queue.pause();
|
|
176
|
+
return true;
|
|
177
|
+
case "resume":
|
|
178
|
+
await queue.resume();
|
|
179
|
+
return true;
|
|
180
|
+
case "drain":
|
|
181
|
+
await queue.drain(valueOrDefault(msg.delayed, false));
|
|
182
|
+
return true;
|
|
183
|
+
case "clean":
|
|
184
|
+
return queue.clean(
|
|
185
|
+
valueOrDefault(msg.grace, 0),
|
|
186
|
+
valueOrDefault(msg.limit, 1000),
|
|
187
|
+
msg.state || "completed",
|
|
188
|
+
);
|
|
189
|
+
case "stopAndRemoveAllJobs":
|
|
190
|
+
return dispatchStopAndRemoveAllJobs(queue);
|
|
191
|
+
case "setGlobalConcurrency":
|
|
192
|
+
await queue.setGlobalConcurrency(msg.concurrency);
|
|
193
|
+
return true;
|
|
194
|
+
case "getGlobalConcurrency":
|
|
195
|
+
return queue.getGlobalConcurrency();
|
|
196
|
+
case "removeGlobalConcurrency":
|
|
197
|
+
return queue.removeGlobalConcurrency();
|
|
198
|
+
case "setGlobalRateLimit":
|
|
199
|
+
await queue.setGlobalRateLimit(msg.max, msg.duration);
|
|
200
|
+
return true;
|
|
201
|
+
case "getGlobalRateLimit":
|
|
202
|
+
return queue.getGlobalRateLimit();
|
|
203
|
+
case "removeGlobalRateLimit":
|
|
204
|
+
return queue.removeGlobalRateLimit();
|
|
205
|
+
case "rateLimit":
|
|
206
|
+
await queue.rateLimit(msg.duration);
|
|
207
|
+
return true;
|
|
208
|
+
case "getRateLimitTtl":
|
|
209
|
+
return queue.getRateLimitTtl(msg.maxJobs);
|
|
210
|
+
case "removeRateLimitKey":
|
|
211
|
+
return queue.removeRateLimitKey();
|
|
212
|
+
case "addJobLog":
|
|
213
|
+
return queue.addJobLog(msg.jobId || msg.jobid, msg.logRow, msg.keepLogs);
|
|
214
|
+
case "getJobLogs":
|
|
215
|
+
return queue.getJobLogs(
|
|
216
|
+
msg.jobId || msg.jobid,
|
|
217
|
+
valueOrDefault(msg.start, 0),
|
|
218
|
+
valueOrDefault(msg.end, -1),
|
|
219
|
+
valueOrDefault(msg.asc, true),
|
|
220
|
+
);
|
|
221
|
+
case "exportPrometheusMetrics":
|
|
222
|
+
return queue.exportPrometheusMetrics();
|
|
223
|
+
default:
|
|
224
|
+
throw new Error(`Unsupported bull cmd: ${cmd}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = {
|
|
229
|
+
dispatchCommand,
|
|
230
|
+
};
|