@pauldeng/node-red-contrib-bullmq 1.0.2 → 2.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/README.md +62 -42
- package/bull-queue.html +300 -91
- package/bull-queue.js +686 -143
- package/docs/ARCHITECTURE.md +63 -17
- package/docs/CHANGE_WORKFLOW.md +10 -11
- package/docs/COMMANDS.md +72 -11
- package/docs/CONNECTIONS.md +74 -7
- package/docs/MIGRATION.md +56 -22
- package/docs/NODE_GUIDE.md +52 -14
- package/docs/REFERENCE_MAP.md +29 -10
- package/docs/RELEASE.md +18 -5
- package/docs/RULES.md +33 -0
- package/docs/TELEMETRY.md +55 -0
- package/docs/TESTING.md +30 -16
- package/docs/TROUBLESHOOTING.md +44 -5
- package/examples/README.md +40 -12
- package/examples/bullmq_features.json +218 -16
- package/examples/example_flow.json +11 -11
- package/examples/postgres_backend.json +149 -0
- package/examples/repeatable_jobs.json +51 -19
- package/examples/scheduled_notifications.json +216 -0
- package/lib/acknowledgements.js +19 -1
- package/lib/commands.js +74 -35
- package/lib/connections.js +271 -48
- package/lib/scheduler.js +5 -74
- package/lib/serialization.js +1 -4
- package/package.json +25 -9
package/bull-queue.html
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
<script type="text/html" data-template-name="
|
|
1
|
+
<script type="text/html" data-template-name="bullmq-queue-server">
|
|
2
2
|
<div class="form-row">
|
|
3
3
|
<label for="node-config-input-name"><i class="fa fa-tasks"></i> Queue</label>
|
|
4
|
-
<input type="text" id="node-config-input-name" placeholder="
|
|
4
|
+
<input type="text" id="node-config-input-name" placeholder="email-jobs">
|
|
5
5
|
</div>
|
|
6
6
|
<div class="form-row">
|
|
7
|
+
<label for="node-config-input-backend"><i class="fa fa-database"></i> Backend</label>
|
|
8
|
+
<select id="node-config-input-backend" style="width: 70%;">
|
|
9
|
+
<option value="redis">Redis</option>
|
|
10
|
+
<option value="postgres">PostgreSQL</option>
|
|
11
|
+
</select>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="form-row bull-redis-row">
|
|
7
14
|
<label for="node-config-input-deployment"><i class="fa fa-server"></i> Deployment</label>
|
|
8
15
|
<select id="node-config-input-deployment" style="width: 70%;">
|
|
9
16
|
<option value="single">Standalone Redis</option>
|
|
@@ -31,13 +38,30 @@
|
|
|
31
38
|
<label for="node-config-input-sentinelMasterName"><i class="fa fa-tag"></i> Master</label>
|
|
32
39
|
<input type="text" id="node-config-input-sentinelMasterName" placeholder="mymaster">
|
|
33
40
|
</div>
|
|
34
|
-
<div class="form-row">
|
|
41
|
+
<div class="form-row bull-db-row">
|
|
35
42
|
<label for="node-config-input-db"><i class="fa fa-database"></i> Database</label>
|
|
36
43
|
<input type="number" id="node-config-input-db" placeholder="0">
|
|
37
44
|
</div>
|
|
45
|
+
<div class="form-row bull-postgres-row">
|
|
46
|
+
<label for="node-config-input-database"><i class="fa fa-database"></i> Database Name</label>
|
|
47
|
+
<input type="text" id="node-config-input-database" placeholder="bullmq">
|
|
48
|
+
</div>
|
|
49
|
+
<div class="form-row bull-postgres-row">
|
|
50
|
+
<label for="node-config-input-schema"><i class="fa fa-folder-o"></i> Schema</label>
|
|
51
|
+
<input type="text" id="node-config-input-schema" placeholder="bullmq">
|
|
52
|
+
</div>
|
|
53
|
+
<div class="form-row bull-postgres-row">
|
|
54
|
+
<label for="node-config-input-max"><i class="fa fa-database"></i> Pool Max</label>
|
|
55
|
+
<input type="number" id="node-config-input-max" min="1" step="1" placeholder="2">
|
|
56
|
+
</div>
|
|
57
|
+
<div class="form-row bull-postgres-row">
|
|
58
|
+
<label for="node-config-input-migrate"><i class="fa fa-wrench"></i> Migrations</label>
|
|
59
|
+
<input type="checkbox" id="node-config-input-migrate" style="display:inline-block; width:auto; vertical-align:top;">
|
|
60
|
+
<span>Create and update the BullMQ schema on connect</span>
|
|
61
|
+
</div>
|
|
38
62
|
<div class="form-row">
|
|
39
63
|
<label for="node-config-input-username"><i class="fa fa-user"></i> Username</label>
|
|
40
|
-
<input type="text" id="node-config-input-username" placeholder="
|
|
64
|
+
<input type="text" id="node-config-input-username" placeholder="user">
|
|
41
65
|
</div>
|
|
42
66
|
<div class="form-row">
|
|
43
67
|
<label for="node-config-input-password"><i class="fa fa-lock"></i> Password</label>
|
|
@@ -54,7 +78,7 @@
|
|
|
54
78
|
<div class="form-row">
|
|
55
79
|
<label for="node-config-input-tls"><i class="fa fa-shield"></i> TLS</label>
|
|
56
80
|
<input type="checkbox" id="node-config-input-tls" style="display:inline-block; width:auto; vertical-align:top;">
|
|
57
|
-
<span>Use TLS for
|
|
81
|
+
<span>Use TLS for data connections</span>
|
|
58
82
|
</div>
|
|
59
83
|
<div class="form-row bull-sentinel-row">
|
|
60
84
|
<label for="node-config-input-sentinelTls"><i class="fa fa-shield"></i> Sentinel TLS</label>
|
|
@@ -68,7 +92,7 @@
|
|
|
68
92
|
</div>
|
|
69
93
|
<div class="form-row bull-tls-row">
|
|
70
94
|
<label for="node-config-input-tlsServerName"><i class="fa fa-globe"></i> TLS Server Name</label>
|
|
71
|
-
<input type="text" id="node-config-input-tlsServerName" placeholder="
|
|
95
|
+
<input type="text" id="node-config-input-tlsServerName" placeholder="server.example.com">
|
|
72
96
|
</div>
|
|
73
97
|
<div class="form-row bull-tls-row">
|
|
74
98
|
<label for="node-config-input-tlsCa"><i class="fa fa-certificate"></i> CA</label>
|
|
@@ -82,30 +106,95 @@
|
|
|
82
106
|
<label for="node-config-input-tlsKey"><i class="fa fa-key"></i> Client Key</label>
|
|
83
107
|
<textarea id="node-config-input-tlsKey" rows="3" style="width: 70%;"></textarea>
|
|
84
108
|
</div>
|
|
85
|
-
<div class="form-row">
|
|
109
|
+
<div class="form-row bull-redis-row">
|
|
86
110
|
<label for="node-config-input-prefix"><i class="fa fa-code"></i> Prefix</label>
|
|
87
111
|
<input type="text" id="node-config-input-prefix" placeholder="{bull}">
|
|
88
112
|
</div>
|
|
113
|
+
<div class="form-row">
|
|
114
|
+
<label for="node-config-input-removeOnComplete"><i class="fa fa-trash"></i> Completed</label>
|
|
115
|
+
<input type="number" id="node-config-input-removeOnComplete" min="0" step="1" placeholder="1000">
|
|
116
|
+
</div>
|
|
117
|
+
<div class="form-row">
|
|
118
|
+
<label for="node-config-input-removeOnFail"><i class="fa fa-trash-o"></i> Failed</label>
|
|
119
|
+
<input type="number" id="node-config-input-removeOnFail" min="0" step="1" placeholder="5000">
|
|
120
|
+
</div>
|
|
121
|
+
<div class="form-row">
|
|
122
|
+
<label for="node-config-input-telemetry"><i class="fa fa-line-chart"></i> Telemetry</label>
|
|
123
|
+
<input type="checkbox" id="node-config-input-telemetry" style="display:inline-block; width:auto; vertical-align:top;">
|
|
124
|
+
<span>Enable OpenTelemetry tracing via bullmq-otel</span>
|
|
125
|
+
</div>
|
|
126
|
+
<div class="form-row bull-telemetry-row">
|
|
127
|
+
<label for="node-config-input-telemetryServiceName"><i class="fa fa-tag"></i> Service Name</label>
|
|
128
|
+
<input type="text" id="node-config-input-telemetryServiceName" placeholder="queue name">
|
|
129
|
+
</div>
|
|
130
|
+
<div class="form-row bull-telemetry-row">
|
|
131
|
+
<label for="node-config-input-telemetryMetrics"><i class="fa fa-bar-chart"></i> Metrics</label>
|
|
132
|
+
<input type="checkbox" id="node-config-input-telemetryMetrics" style="display:inline-block; width:auto; vertical-align:top;">
|
|
133
|
+
<span>Enable OpenTelemetry metrics</span>
|
|
134
|
+
</div>
|
|
89
135
|
</script>
|
|
90
136
|
|
|
91
137
|
<script type="text/javascript">
|
|
92
138
|
(function() {
|
|
139
|
+
function validateKeepCount(value) {
|
|
140
|
+
return value === "" || /^\d+$/.test(value);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Blank is not zero: blank means "use the default pool size", while 0 is a
|
|
144
|
+
// pool that can never hand out a connection, which the runtime rejects.
|
|
145
|
+
// Undefined counts as blank so a saved flow with no max never shows an
|
|
146
|
+
// error before the dialog has been opened once.
|
|
147
|
+
function validatePoolMax(value) {
|
|
148
|
+
if (value === "" || value === undefined || value === null) {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
return /^\d+$/.test(String(value)) && Number(value) >= 1;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function isPostgresBackend() {
|
|
155
|
+
return $("#node-config-input-backend").val() === "postgres";
|
|
156
|
+
}
|
|
157
|
+
|
|
93
158
|
function updateBullQueueServerRows() {
|
|
159
|
+
var postgres = isPostgresBackend();
|
|
94
160
|
var deployment = $("#node-config-input-deployment").val() || "single";
|
|
95
|
-
$(".bull-
|
|
96
|
-
$(".bull-
|
|
97
|
-
|
|
161
|
+
$(".bull-redis-row").toggle(!postgres);
|
|
162
|
+
$(".bull-postgres-row").toggle(postgres);
|
|
163
|
+
// Host and port are shared, so PostgreSQL always shows them. The cluster,
|
|
164
|
+
// Sentinel, and Redis-database rows describe Redis topologies only.
|
|
165
|
+
$(".bull-single-row").toggle(postgres || deployment === "single");
|
|
166
|
+
$(".bull-cluster-row").toggle(!postgres && deployment === "cluster");
|
|
167
|
+
$(".bull-sentinel-row").toggle(!postgres && deployment === "sentinel");
|
|
168
|
+
$(".bull-db-row").toggle(!postgres && deployment !== "cluster");
|
|
98
169
|
$(".bull-tls-row").toggle($("#node-config-input-tls").is(":checked"));
|
|
170
|
+
$(".bull-telemetry-row").toggle($("#node-config-input-telemetry").is(":checked"));
|
|
99
171
|
}
|
|
100
172
|
|
|
101
|
-
RED.nodes.registerType("
|
|
173
|
+
RED.nodes.registerType("bullmq-queue-server", {
|
|
102
174
|
category: "config",
|
|
103
175
|
defaults: {
|
|
104
176
|
name: { value: "", required: true },
|
|
177
|
+
backend: { value: "redis", required: true },
|
|
105
178
|
deployment: { value: "single", required: true },
|
|
106
179
|
address: { value: "localhost" },
|
|
107
180
|
port: { value: 6379, validate: RED.validators.number() },
|
|
108
181
|
db: { value: "" },
|
|
182
|
+
database: { value: "" },
|
|
183
|
+
schema: { value: "" },
|
|
184
|
+
max: {
|
|
185
|
+
value: "",
|
|
186
|
+
// Node-RED validates every default, visible or not, and calls this
|
|
187
|
+
// with the node as `this`. A Redis config must not be flagged
|
|
188
|
+
// invalid by the PostgreSQL pool size: that row is hidden on Redis,
|
|
189
|
+
// so the dialog offers no way to correct it. lib/connections.js
|
|
190
|
+
// draws the same line, validating only the selected backend's
|
|
191
|
+
// fields. A flow saved before the selector existed has no backend
|
|
192
|
+
// property and is Redis, so it is exempt too.
|
|
193
|
+
validate: function(value) {
|
|
194
|
+
return this.backend !== "postgres" || validatePoolMax(value);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
migrate: { value: true },
|
|
109
198
|
clusterNodes: { value: "" },
|
|
110
199
|
sentinels: { value: "" },
|
|
111
200
|
sentinelMasterName: { value: "" },
|
|
@@ -115,7 +204,12 @@
|
|
|
115
204
|
sentinelTls: { value: false },
|
|
116
205
|
tlsRejectUnauthorized: { value: true },
|
|
117
206
|
tlsServerName: { value: "" },
|
|
118
|
-
prefix: { value: "" }
|
|
207
|
+
prefix: { value: "" },
|
|
208
|
+
removeOnComplete: { value: "1000", validate: validateKeepCount },
|
|
209
|
+
removeOnFail: { value: "5000", validate: validateKeepCount },
|
|
210
|
+
telemetry: { value: false },
|
|
211
|
+
telemetryServiceName: { value: "" },
|
|
212
|
+
telemetryMetrics: { value: false }
|
|
119
213
|
},
|
|
120
214
|
credentials: {
|
|
121
215
|
password: { type: "password" },
|
|
@@ -128,49 +222,92 @@
|
|
|
128
222
|
return this.name || this.address + ":" + this.port;
|
|
129
223
|
},
|
|
130
224
|
oneditprepare: function() {
|
|
225
|
+
// A flow saved before the backend selector existed has no backend or
|
|
226
|
+
// migrate property, and editor defaults never migrate saved JSON: the
|
|
227
|
+
// inputs are populated from the saved node, so both would read as
|
|
228
|
+
// "unset" here -- an empty select, and a migrations box unchecked when
|
|
229
|
+
// the runtime default is on. Seed them to the runtime's own defaults
|
|
230
|
+
// before anything reads them.
|
|
231
|
+
if (!this.backend) {
|
|
232
|
+
$("#node-config-input-backend").val("redis");
|
|
233
|
+
}
|
|
234
|
+
if (this.migrate === undefined) {
|
|
235
|
+
$("#node-config-input-migrate").prop("checked", true);
|
|
236
|
+
}
|
|
237
|
+
// One saved port field serves both backends. Keep one value per mode
|
|
238
|
+
// while the dialog is open so even a deliberate Redis port of 5432
|
|
239
|
+
// survives a round-trip through PostgreSQL.
|
|
240
|
+
var backendField = $("#node-config-input-backend");
|
|
241
|
+
var portField = $("#node-config-input-port");
|
|
242
|
+
var activeBackend = isPostgresBackend() ? "postgres" : "redis";
|
|
243
|
+
var portValues = {
|
|
244
|
+
redis: activeBackend === "redis" ? String(portField.val()) : "6379",
|
|
245
|
+
postgres: activeBackend === "postgres" ? String(portField.val()) : "5432"
|
|
246
|
+
};
|
|
247
|
+
backendField.on("change", function() {
|
|
248
|
+
var nextBackend = isPostgresBackend() ? "postgres" : "redis";
|
|
249
|
+
if (nextBackend !== activeBackend) {
|
|
250
|
+
portValues[activeBackend] = String(portField.val());
|
|
251
|
+
portField.val(portValues[nextBackend]);
|
|
252
|
+
activeBackend = nextBackend;
|
|
253
|
+
}
|
|
254
|
+
updateBullQueueServerRows();
|
|
255
|
+
});
|
|
131
256
|
$("#node-config-input-deployment").on("change", updateBullQueueServerRows);
|
|
132
257
|
$("#node-config-input-tls").on("change", updateBullQueueServerRows);
|
|
258
|
+
$("#node-config-input-telemetry").on("change", updateBullQueueServerRows);
|
|
133
259
|
updateBullQueueServerRows();
|
|
134
260
|
}
|
|
135
261
|
});
|
|
136
262
|
})();
|
|
137
263
|
</script>
|
|
138
264
|
|
|
139
|
-
<script type="text/html" data-help-name="
|
|
140
|
-
<p>Configures the BullMQ queue name and
|
|
141
|
-
<p>
|
|
265
|
+
<script type="text/html" data-help-name="bullmq-queue-server">
|
|
266
|
+
<p>Configures the BullMQ queue name and the backend connection shared by the BullMQ nodes. BullMQ v6 can store queues in either Redis or PostgreSQL; pick one per config node with <b>Backend</b>.</p>
|
|
267
|
+
<p>PostgreSQL requires the <code>pg</code> package, which is an optional peer dependency and must be installed separately (<code>npm install pg</code>). It is reported once per config node on first use, not at load.</p>
|
|
268
|
+
<p>API reference: <a href="https://docs.bullmq.io/api/classes/v6.Queue.html" target="_blank" rel="noopener noreferrer">Queue</a> and <a href="https://docs.bullmq.io/api/interfaces/v6.QueueOptions.html" target="_blank" rel="noopener noreferrer">QueueOptions</a>.</p>
|
|
142
269
|
|
|
143
270
|
<h3>Options</h3>
|
|
144
271
|
<ul>
|
|
145
272
|
<li><b>Queue</b>: BullMQ queue name. All producer, worker, event, and flow nodes that select this config use this queue name unless a BullMQ flow child specifies another <code>queueName</code>.</li>
|
|
273
|
+
<li><b>Backend</b>: Where BullMQ stores this queue. <code>Redis</code> keeps the Redis topology, key prefix, and Redis database options below. <code>PostgreSQL</code> replaces them with the database options and needs <code>pg</code> installed.</li>
|
|
146
274
|
<li><b>Deployment</b>: Redis topology. Use <code>Standalone Redis</code> for one Redis endpoint, <code>Redis Cluster / AWS MemoryDB</code> for cluster-compatible endpoints, or <code>Redis Sentinel</code> for Sentinel discovery.</li>
|
|
147
|
-
<li><b>Host</b>: Standalone Redis host name or IP address.</li>
|
|
148
|
-
<li><b>Port</b>: Standalone Redis port. The
|
|
275
|
+
<li><b>Host</b>: Standalone Redis host, or the PostgreSQL server host name or IP address.</li>
|
|
276
|
+
<li><b>Port</b>: Standalone Redis port, or the PostgreSQL port. The defaults are <code>6379</code> for Redis and <code>5432</code> for PostgreSQL. While the dialog is open, switching <b>Backend</b> preserves a separate value for each backend so a custom port is never overwritten.</li>
|
|
149
277
|
<li><b>Cluster Nodes</b>: Comma or newline separated cluster seed nodes such as <code>redis-a:6379, redis-b:6379</code>. AWS MemoryDB cluster configuration endpoints are entered here.</li>
|
|
150
278
|
<li><b>Sentinels</b>: Comma or newline separated Sentinel endpoints such as <code>sentinel-a:26379, sentinel-b:26379</code>.</li>
|
|
151
279
|
<li><b>Master</b>: Sentinel master name, for example <code>mymaster</code>.</li>
|
|
152
280
|
<li><b>Database</b>: Redis database number for standalone or Sentinel data connections. Cluster and MemoryDB deployments use Redis database <code>0</code>.</li>
|
|
153
|
-
<li><b>
|
|
154
|
-
<li><b>
|
|
281
|
+
<li><b>Database Name</b>: PostgreSQL database that holds the BullMQ schema. Distinct from the Redis <b>Database</b> number above, which is an index rather than a name. Left blank, node-postgres falls back to the <code>PGDATABASE</code> environment variable and then to the operating-system user name, as it does for a blank <b>Username</b> with <code>PGUSER</code>.</li>
|
|
282
|
+
<li><b>Schema</b>: PostgreSQL schema for BullMQ's tables. Defaults to <code>bullmq</code>. Separate schemas keep independent queue sets in one database.</li>
|
|
283
|
+
<li><b>Pool Max</b>: Maximum PostgreSQL connections in each pool. Blank uses the default of <code>2</code>; <code>0</code> is rejected, since a pool that can never hand out a connection cannot run a queue. BullMQ builds one pool per queue, worker, event listener, and flow producer; workers and event listeners also hold one extra <code>LISTEN</code> connection each. A config node feeding all four therefore costs up to 4 × <b>Pool Max</b> + 2 server connections — 10 at the default. Start with the default and raise it only when observed pool waits justify the extra server connections; worker <b>Concurrency</b> does not require a one-to-one pool size.</li>
|
|
284
|
+
<li><b>Migrations</b>: Creates and updates BullMQ's PostgreSQL schema when connecting. On by default. Turn it off where the database user is not allowed to change the schema, and apply BullMQ's migrations separately -- a queue pointed at a database whose schema is missing or outdated reports that as an error rather than running.</li>
|
|
285
|
+
<li><b>Username</b>: Redis ACL username, or the PostgreSQL user. Leave empty to use the client's backend default; this is also correct for Redis servers that only use a password.</li>
|
|
286
|
+
<li><b>Password</b>: Password for data connections, for either backend. Stored as a Node-RED credential.</li>
|
|
155
287
|
<li><b>Sentinel User</b>: Redis ACL username for Sentinel discovery connections when Sentinels require separate credentials.</li>
|
|
156
288
|
<li><b>Sentinel Pass</b>: Password for Sentinel discovery connections. Stored as a Node-RED credential.</li>
|
|
157
|
-
<li><b>TLS</b>: Enables TLS for
|
|
289
|
+
<li><b>TLS</b>: Enables TLS for data connections on either backend. Use this for AWS MemoryDB, any Redis deployment that requires encrypted data connections, and PostgreSQL servers that require SSL. <b>Verify TLS</b>, <b>TLS Server Name</b>, <b>CA</b>, <b>Client Cert</b>, and <b>Client Key</b> apply to both.</li>
|
|
158
290
|
<li><b>Sentinel TLS</b>: Enables TLS for Sentinel discovery connections.</li>
|
|
159
291
|
<li><b>Verify TLS</b>: Rejects certificates that cannot be verified. Keep enabled in production unless you intentionally use a private test certificate.</li>
|
|
160
|
-
<li><b>TLS Server Name</b>: Optional server name used for certificate validation when it differs from the host value.</li>
|
|
161
|
-
<li><b>CA</b>: PEM certificate authority text for validating private
|
|
292
|
+
<li><b>TLS Server Name</b>: Optional server name used for certificate validation when it differs from the host value. For PostgreSQL, node-postgres uses the connection host when it is a hostname and honors this override only when <b>Host</b> is a literal IP address.</li>
|
|
293
|
+
<li><b>CA</b>: PEM certificate authority text for validating private backend certificates. Stored as a Node-RED credential.</li>
|
|
162
294
|
<li><b>Client Cert</b>: PEM client certificate for mutual TLS deployments. Stored as a Node-RED credential.</li>
|
|
163
295
|
<li><b>Client Key</b>: PEM client private key for mutual TLS deployments. Stored as a Node-RED credential.</li>
|
|
164
|
-
<li><b>Prefix</b>: BullMQ key prefix. Redis Cluster and MemoryDB prefixes must contain a hash tag such as <code>{bull}</code
|
|
296
|
+
<li><b>Prefix</b>: BullMQ key prefix. Redis Cluster and MemoryDB prefixes must contain a hash tag such as <code>{bull}</code>. Independent queues may use different tags, but prefixes in one flow tree or bulk flow batch must contain the same hash tag for atomic operations. Each worker must use the exact prefix assigned to its queue in that flow.</li>
|
|
297
|
+
<li><b>Keep Completed</b> / <b>Keep Failed</b>: Queue-level auto-removal, applied as BullMQ <code>defaultJobOptions</code>. Each keeps that many of the newest jobs in the state and removes older ones. Leaving a field blank keeps every job, which is BullMQ's own default and grows backend storage without bound, so blank is only appropriate when something else prunes the queue. <code>msg.jobopts</code> overrides these defaults for <code>bullmq cmd</code>. For <code>bullmq flow</code>, per-queue <code>msg.flowopts.queuesOptions</code> defaults and then each job's <code>opts</code> take precedence.</li>
|
|
298
|
+
<li><b>Telemetry</b>: Enables OpenTelemetry tracing for this queue's <code>Queue</code>, <code>Worker</code>, and <code>FlowProducer</code> instances using <a href="https://www.npmjs.com/package/bullmq-otel" target="_blank" rel="noopener noreferrer">bullmq-otel</a>. Off by default. <code>bullmq-otel</code> is an optional dependency and must be installed separately (<code>npm install bullmq-otel</code>) before enabling this switch. This node does not start or configure an OpenTelemetry SDK or exporter; the host Node-RED process owns that (for example NODE_OPTIONS auto-instrumentation, or an SDK bootstrapped from Node-RED's <code>settings.js</code>). When no OpenTelemetry provider is registered, <code>bullmq-otel</code> simply no-ops rather than erroring.</li>
|
|
299
|
+
<li><b>Service Name</b>: Tracer and meter name reported to OpenTelemetry. Leave blank to use the <b>Queue</b> name.</li>
|
|
300
|
+
<li><b>Metrics</b>: Enables OpenTelemetry metrics in addition to tracing. A <code>MeterProvider</code> must be registered with the host process before this queue is first used, otherwise metrics are dropped. For metrics scraping without any OpenTelemetry stack, the <code>bullmq cmd</code> node's <code>msg.cmd</code> <code>exportPrometheusMetrics</code> is a zero-dependency alternative.</li>
|
|
165
301
|
</ul>
|
|
166
302
|
|
|
167
303
|
<h3>Example</h3>
|
|
168
304
|
<p>For local Redis, set <b>Queue</b> to <code>email-jobs</code>, <b>Deployment</b> to <code>Standalone Redis</code>, <b>Host</b> to <code>localhost</code>, and <b>Port</b> to <code>6379</code>.</p>
|
|
305
|
+
<p>For local PostgreSQL, set <b>Backend</b> to <code>PostgreSQL</code>, <b>Host</b> to <code>localhost</code>, <b>Port</b> to <code>5432</code>, <b>Database Name</b> to <code>bullmq</code>, and leave <b>Schema</b>, <b>Pool Max</b>, and <b>Migrations</b> at their defaults. <b>Deployment</b>, <b>Prefix</b>, and the Redis <b>Database</b> number do not apply.</p>
|
|
169
306
|
<p>For AWS MemoryDB, set <b>Deployment</b> to <code>Redis Cluster / AWS MemoryDB</code>, enter the cluster endpoint in <b>Cluster Nodes</b>, enable <b>TLS</b>, keep <b>Verify TLS</b> enabled, and use <code>{bull}</code> as <b>Prefix</b>.</p>
|
|
170
|
-
<p>Redis
|
|
307
|
+
<p>Redis must use <code>maxmemory-policy=noeviction</code> and durable persistence; BullMQ recommends Append Only File (AOF) for self-managed Redis. Job data is stored in clear text, so avoid sensitive payloads or encrypt sensitive fields before adding a job. Bull v4 Redis queue data is not automatically migrated to BullMQ.</p>
|
|
171
308
|
</script>
|
|
172
309
|
|
|
173
|
-
<script type="text/html" data-template-name="
|
|
310
|
+
<script type="text/html" data-template-name="bullmq cmd">
|
|
174
311
|
<div class="form-row">
|
|
175
312
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
176
313
|
<input type="text" id="node-input-name">
|
|
@@ -181,9 +318,9 @@
|
|
|
181
318
|
</div>
|
|
182
319
|
</script>
|
|
183
320
|
|
|
184
|
-
<script type="text/html" data-help-name="
|
|
321
|
+
<script type="text/html" data-help-name="bullmq cmd">
|
|
185
322
|
<p>Executes BullMQ producer and queue-administration commands from an input message.</p>
|
|
186
|
-
<p>API reference: <a href="https://
|
|
323
|
+
<p>API reference: <a href="https://docs.bullmq.io/api/classes/v6.Queue.html" target="_blank" rel="noopener noreferrer">Queue</a>, <a href="https://docs.bullmq.io/api/classes/v6.Queue.html#add" target="_blank" rel="noopener noreferrer">Queue.add</a>, <a href="https://docs.bullmq.io/api/types/v6.JobsOptions.html" target="_blank" rel="noopener noreferrer">JobsOptions</a>, <a href="https://docs.bullmq.io/api/types/v6.DeduplicationOptions.html" target="_blank" rel="noopener noreferrer">DeduplicationOptions</a>, <a href="https://docs.bullmq.io/api/classes/v6.Queue.html#setglobalratelimit" target="_blank" rel="noopener noreferrer">Queue.setGlobalRateLimit</a>, and <a href="https://docs.bullmq.io/api/classes/v6.Queue.html#upsertjobscheduler" target="_blank" rel="noopener noreferrer">Queue.upsertJobScheduler</a>.</p>
|
|
187
324
|
|
|
188
325
|
<h3>Options</h3>
|
|
189
326
|
<ul>
|
|
@@ -193,11 +330,11 @@
|
|
|
193
330
|
|
|
194
331
|
<h3>Input message</h3>
|
|
195
332
|
<ul>
|
|
196
|
-
<li><code>msg.cmd</code>: Command name. Defaults to <code>add</code>. Common commands include <code>add</code>, <code>addBulk</code>, <code>getJob</code>, <code>getJobs</code>, <code>removeJob</code>, <code>retryJob</code>, <code>getDelayed</code>, <code>promoteJob</code>, <code>getPrioritized</code>, <code>changePriority</code>, <code>setGlobalRateLimit</code>, <code>removeGlobalRateLimit</code>, <code>upsertJobScheduler</code>, and <code>
|
|
197
|
-
<li><code>msg.payload</code>: Job data for <code>add</code> when <code>msg.jobData</code> is not set. For some commands it carries command parameters, such as <code>{ "max": 2, "duration": 1000 }</code> for <code>setGlobalRateLimit</code
|
|
333
|
+
<li><code>msg.cmd</code>: Command name. Defaults to <code>add</code>. Common commands include <code>add</code>, <code>addBulk</code>, <code>getJob</code>, <code>getJobs</code>, <code>removeJob</code>, <code>retryJob</code>, <code>getDelayed</code>, <code>promoteJob</code>, <code>getPrioritized</code>, <code>changePriority</code>, <code>setGlobalRateLimit</code>, <code>removeGlobalRateLimit</code>, <code>upsertJobScheduler</code>, <code>removeJobScheduler</code>, <code>isPaused</code>, <code>isMaxed</code>, and <code>getVersion</code>.</li>
|
|
334
|
+
<li><code>msg.payload</code>: Job data for <code>add</code> when <code>msg.jobData</code> is not set. For some commands it carries command parameters, such as <code>{ "max": 2, "duration": 1000 }</code> for <code>setGlobalRateLimit</code>. <code>isPaused</code> and <code>isMaxed</code> return a boolean; <code>getVersion</code> returns BullMQ's queue metadata version string (for example <code>bullmq:6.3.1</code>), not the Redis server version.</li>
|
|
198
335
|
<li><code>msg.jobName</code>: BullMQ job name for <code>add</code>. Defaults to <code>default</code>.</li>
|
|
199
336
|
<li><code>msg.jobData</code>: Full job data object for <code>add</code>. When set, it takes precedence over <code>msg.payload</code>.</li>
|
|
200
|
-
<li><code>msg.jobopts</code>: BullMQ job options for <code>add</code>, including <code>delay</code>, <code>priority</code>, <code>deduplication</code>, <code>attempts</code>, <code>backoff</code>, <code>removeOnComplete</code
|
|
337
|
+
<li><code>msg.jobopts</code>: BullMQ job options for <code>add</code>, including <code>delay</code>, <code>priority</code>, <code>deduplication</code>, <code>attempts</code>, <code>backoff</code>, and <code>removeOnComplete</code>. Repeat options are rejected; use <code>upsertJobScheduler</code>.</li>
|
|
201
338
|
</ul>
|
|
202
339
|
|
|
203
340
|
<h3>Examples</h3>
|
|
@@ -210,6 +347,14 @@ msg.jobopts = {
|
|
|
210
347
|
priority: 1,
|
|
211
348
|
removeOnComplete: true
|
|
212
349
|
};
|
|
350
|
+
return msg;</pre>
|
|
351
|
+
<p>Add three one-off jobs that become eligible after 10, 20, and 30 seconds:</p>
|
|
352
|
+
<pre>msg.cmd = "addBulk";
|
|
353
|
+
msg.payload = [
|
|
354
|
+
{ name: "delayed-1", data: { payload: "first" }, opts: { delay: 10000 } },
|
|
355
|
+
{ name: "delayed-2", data: { payload: "second" }, opts: { delay: 20000 } },
|
|
356
|
+
{ name: "delayed-3", data: { payload: "third" }, opts: { delay: 30000 } }
|
|
357
|
+
];
|
|
213
358
|
return msg;</pre>
|
|
214
359
|
<p>Add one job per deduplication id:</p>
|
|
215
360
|
<pre>msg.cmd = "add";
|
|
@@ -224,36 +369,37 @@ return msg;</pre>
|
|
|
224
369
|
msg.payload = { max: 2, duration: 1000 };
|
|
225
370
|
return msg;</pre>
|
|
226
371
|
<p>Add or update a scheduler every minute:</p>
|
|
227
|
-
<pre>msg.cmd = "
|
|
228
|
-
msg.
|
|
229
|
-
msg.
|
|
230
|
-
msg.
|
|
231
|
-
|
|
232
|
-
|
|
372
|
+
<pre>msg.cmd = "upsertJobScheduler";
|
|
373
|
+
msg.schedulerId = "heartbeat-every-minute";
|
|
374
|
+
msg.repeat = { pattern: "*/1 * * * *" };
|
|
375
|
+
msg.template = {
|
|
376
|
+
name: "heartbeat",
|
|
377
|
+
data: { payload: "scheduled heartbeat" }
|
|
233
378
|
};
|
|
234
379
|
return msg;</pre>
|
|
235
|
-
<p>
|
|
380
|
+
<p>Job Scheduler commands use the native BullMQ v6 fields: <code>msg.schedulerId</code>, <code>msg.repeat.pattern</code>, optional <code>msg.repeat.tz</code>, and <code>msg.template</code>.</p>
|
|
381
|
+
<p>BullMQ v6 removed the <code>paused</code> job state, so <code>getJobCounts</code> no longer includes a <code>paused</code> key in its result.</p>
|
|
236
382
|
</script>
|
|
237
383
|
|
|
238
384
|
<script type="text/javascript">
|
|
239
|
-
RED.nodes.registerType("
|
|
385
|
+
RED.nodes.registerType("bullmq cmd", {
|
|
240
386
|
color: "#ffffff",
|
|
241
387
|
category: "function",
|
|
242
388
|
defaults: {
|
|
243
389
|
name: { value: "" },
|
|
244
|
-
queue: { type: "
|
|
390
|
+
queue: { type: "bullmq-queue-server", required: true }
|
|
245
391
|
},
|
|
246
392
|
inputs: 1,
|
|
247
393
|
outputs: 1,
|
|
248
394
|
align: "right",
|
|
249
395
|
icon: "bull_icon.png",
|
|
250
396
|
label: function() {
|
|
251
|
-
return this.name || "
|
|
397
|
+
return this.name || "bullmq cmd";
|
|
252
398
|
}
|
|
253
399
|
});
|
|
254
400
|
</script>
|
|
255
401
|
|
|
256
|
-
<script type="text/html" data-template-name="
|
|
402
|
+
<script type="text/html" data-template-name="bullmq run">
|
|
257
403
|
<div class="form-row">
|
|
258
404
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
259
405
|
<input type="text" id="node-input-name">
|
|
@@ -269,13 +415,17 @@ return msg;</pre>
|
|
|
269
415
|
<option value="manual">Manual acknowledgement</option>
|
|
270
416
|
</select>
|
|
271
417
|
</div>
|
|
272
|
-
<div class="form-row">
|
|
418
|
+
<div class="form-row bull-ack-timeout-row">
|
|
273
419
|
<label for="node-input-ackTimeout"><i class="fa fa-clock-o"></i> Ack Timeout</label>
|
|
274
420
|
<input type="number" id="node-input-ackTimeout" placeholder="300000">
|
|
275
421
|
</div>
|
|
276
422
|
<div class="form-row">
|
|
277
423
|
<label for="node-input-concurrency"><i class="fa fa-random"></i> Concurrency</label>
|
|
278
|
-
<input type="number" id="node-input-concurrency" placeholder="1">
|
|
424
|
+
<input type="number" id="node-input-concurrency" min="1" step="1" placeholder="1">
|
|
425
|
+
</div>
|
|
426
|
+
<div class="form-row">
|
|
427
|
+
<label for="node-input-maxStartedAttempts"><i class="fa fa-repeat"></i> Max Started Attempts</label>
|
|
428
|
+
<input type="number" id="node-input-maxStartedAttempts" min="1" step="1" placeholder="100">
|
|
279
429
|
</div>
|
|
280
430
|
<div class="form-row">
|
|
281
431
|
<label for="node-input-limiterMax"><i class="fa fa-tachometer"></i> Limiter Max</label>
|
|
@@ -287,17 +437,18 @@ return msg;</pre>
|
|
|
287
437
|
</div>
|
|
288
438
|
</script>
|
|
289
439
|
|
|
290
|
-
<script type="text/html" data-help-name="
|
|
440
|
+
<script type="text/html" data-help-name="bullmq run">
|
|
291
441
|
<p>Creates a BullMQ Worker for the configured queue and emits each job as a Node-RED message.</p>
|
|
292
|
-
<p>API reference: <a href="https://
|
|
442
|
+
<p>API reference: <a href="https://docs.bullmq.io/api/classes/v6.Worker.html" target="_blank" rel="noopener noreferrer">Worker</a> and <a href="https://docs.bullmq.io/api/interfaces/v6.WorkerOptions.html" target="_blank" rel="noopener noreferrer">WorkerOptions</a>.</p>
|
|
293
443
|
|
|
294
444
|
<h3>Options</h3>
|
|
295
445
|
<ul>
|
|
296
446
|
<li><b>Name</b>: Optional label shown in the Node-RED workspace.</li>
|
|
297
447
|
<li><b>Queue</b>: Queue config node used by the worker.</li>
|
|
298
|
-
<li><b>Completion</b>: <code>Immediate</code> completes the BullMQ job after the message is emitted. <code>Manual acknowledgement</code> keeps the job active until a downstream <code>
|
|
448
|
+
<li><b>Completion</b>: <code>Immediate</code> completes the BullMQ job after the message is emitted. <code>Manual acknowledgement</code> keeps the job active until a downstream <code>bullmq job</code> node completes, fails, or updates it.</li>
|
|
299
449
|
<li><b>Ack Timeout</b>: Maximum time in milliseconds to wait for manual acknowledgement before the node fails the active job. Set to <code>0</code> to wait indefinitely.</li>
|
|
300
450
|
<li><b>Concurrency</b>: Number of jobs this worker can process at the same time.</li>
|
|
451
|
+
<li><b>Max Started Attempts</b>: Maximum times one job may start processing. Defaults to <code>100</code>. BullMQ then fails the job unrecoverably, which bounds loops created by repeated <code>moveToWait</code> or <code>moveToDelayed</code> actions.</li>
|
|
301
452
|
<li><b>Limiter Max</b>: Optional worker-side rate limit count.</li>
|
|
302
453
|
<li><b>Limiter Duration</b>: Optional worker-side rate limit window in milliseconds. Set with <b>Limiter Max</b>.</li>
|
|
303
454
|
</ul>
|
|
@@ -306,38 +457,78 @@ return msg;</pre>
|
|
|
306
457
|
<ul>
|
|
307
458
|
<li><code>msg.payload</code>: <code>job.data.payload</code> when present, otherwise the full BullMQ job data.</li>
|
|
308
459
|
<li><code>msg.job</code>: Serialized job metadata, including id, name, queue name, state-related timestamps, progress, attempts, and options.</li>
|
|
309
|
-
<li><code>msg.bull</code>: Queue and acknowledgement context used by downstream <code>
|
|
460
|
+
<li><code>msg.bull</code>: Queue and acknowledgement context used by downstream <code>bullmq job</code> nodes.</li>
|
|
310
461
|
</ul>
|
|
311
462
|
|
|
463
|
+
<p>BullMQ v6 removed the <code>paused</code> job state: <code>job.getState()</code> never returns <code>"paused"</code>, so a job on a paused queue reports as <code>"waiting"</code>.</p>
|
|
464
|
+
<p>Only jobs from a <b>Completion</b> <code>Manual acknowledgement</code> worker can be cancelled through <code>bullmq job</code>'s <code>cancelJob</code> and <code>cancelAllJobs</code> actions, since cancellation is scoped to <code>msg.bull.ackId</code>.</p>
|
|
465
|
+
|
|
312
466
|
<h3>Example</h3>
|
|
313
467
|
<p>For fire-and-forget processing, use <b>Completion</b> <code>Immediate</code> and wire the output to your processing flow.</p>
|
|
314
|
-
<p>For a flow that must decide success or failure, use <b>Completion</b> <code>Manual acknowledgement</code>, wire the output to processing nodes, then wire into <code>
|
|
468
|
+
<p>For a flow that must decide success or failure, use <b>Completion</b> <code>Manual acknowledgement</code>, wire the output to processing nodes, then wire into <code>bullmq job</code> with <b>Action</b> <code>complete</code> or <code>fail</code>.</p>
|
|
315
469
|
</script>
|
|
316
470
|
|
|
317
471
|
<script type="text/javascript">
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
queue: { type: "bull-queue-server", required: true },
|
|
324
|
-
completionMode: { value: "immediate" },
|
|
325
|
-
ackTimeout: { value: 300000, validate: RED.validators.number() },
|
|
326
|
-
concurrency: { value: 1, validate: RED.validators.number() },
|
|
327
|
-
limiterMax: { value: "" },
|
|
328
|
-
limiterDuration: { value: "" }
|
|
329
|
-
},
|
|
330
|
-
inputs: 0,
|
|
331
|
-
outputs: 1,
|
|
332
|
-
align: "left",
|
|
333
|
-
icon: "bull_icon.png",
|
|
334
|
-
label: function() {
|
|
335
|
-
return this.name || "bull run";
|
|
472
|
+
(function() {
|
|
473
|
+
function updateBullRunRows() {
|
|
474
|
+
$(".bull-ack-timeout-row").toggle(
|
|
475
|
+
$("#node-input-completionMode").val() === "manual"
|
|
476
|
+
);
|
|
336
477
|
}
|
|
337
|
-
|
|
478
|
+
|
|
479
|
+
function positiveInteger(value) {
|
|
480
|
+
var parsed = Number(value);
|
|
481
|
+
return value !== "" && Number.isInteger(parsed) && parsed > 0;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function optionalPositivePair(value, otherProperty) {
|
|
485
|
+
var input = $("#node-input-" + otherProperty);
|
|
486
|
+
var other = input.length ? input.val() : this[otherProperty];
|
|
487
|
+
return (
|
|
488
|
+
((value === "" || value == null) && (other === "" || other == null)) ||
|
|
489
|
+
(positiveInteger(value) && positiveInteger(other))
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
RED.nodes.registerType("bullmq run", {
|
|
494
|
+
color: "#ffffff",
|
|
495
|
+
category: "function",
|
|
496
|
+
defaults: {
|
|
497
|
+
name: { value: "" },
|
|
498
|
+
queue: { type: "bullmq-queue-server", required: true },
|
|
499
|
+
completionMode: { value: "immediate" },
|
|
500
|
+
ackTimeout: { value: 300000, validate: RED.validators.number() },
|
|
501
|
+
concurrency: { value: 1, validate: positiveInteger },
|
|
502
|
+
maxStartedAttempts: { value: 100, validate: positiveInteger },
|
|
503
|
+
limiterMax: {
|
|
504
|
+
value: "",
|
|
505
|
+
validate: function(value) {
|
|
506
|
+
return optionalPositivePair.call(this, value, "limiterDuration");
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
limiterDuration: {
|
|
510
|
+
value: "",
|
|
511
|
+
validate: function(value) {
|
|
512
|
+
return optionalPositivePair.call(this, value, "limiterMax");
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
inputs: 0,
|
|
517
|
+
outputs: 1,
|
|
518
|
+
align: "left",
|
|
519
|
+
icon: "bull_icon.png",
|
|
520
|
+
label: function() {
|
|
521
|
+
return this.name || "bullmq run";
|
|
522
|
+
},
|
|
523
|
+
oneditprepare: function() {
|
|
524
|
+
$("#node-input-completionMode").on("change", updateBullRunRows);
|
|
525
|
+
updateBullRunRows();
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
})();
|
|
338
529
|
</script>
|
|
339
530
|
|
|
340
|
-
<script type="text/html" data-template-name="
|
|
531
|
+
<script type="text/html" data-template-name="bullmq job">
|
|
341
532
|
<div class="form-row">
|
|
342
533
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
343
534
|
<input type="text" id="node-input-name">
|
|
@@ -354,13 +545,18 @@ return msg;</pre>
|
|
|
354
545
|
<option value="getChildrenValues">Get children values</option>
|
|
355
546
|
<option value="getFailedChildrenValues">Get failed children values</option>
|
|
356
547
|
<option value="removeUnprocessedChildren">Remove unprocessed children</option>
|
|
548
|
+
<option value="cancelJob">Cancel job</option>
|
|
549
|
+
<option value="cancelAllJobs">Cancel all jobs</option>
|
|
550
|
+
<option value="moveToWait">Requeue (moveToWait)</option>
|
|
551
|
+
<option value="moveToDelayed">Delay and resume (moveToDelayed)</option>
|
|
552
|
+
<option value="updateData">Update job data</option>
|
|
357
553
|
</select>
|
|
358
554
|
</div>
|
|
359
555
|
</script>
|
|
360
556
|
|
|
361
|
-
<script type="text/html" data-help-name="
|
|
362
|
-
<p>Acts on an active job emitted by a manual-mode <code>
|
|
363
|
-
<p>API reference: <a href="https://
|
|
557
|
+
<script type="text/html" data-help-name="bullmq job">
|
|
558
|
+
<p>Acts on an active job emitted by a manual-mode <code>bullmq run</code> node.</p>
|
|
559
|
+
<p>API reference: <a href="https://docs.bullmq.io/api/classes/v6.Job.html" target="_blank" rel="noopener noreferrer">Job</a>, <a href="https://docs.bullmq.io/api/classes/v6.UnrecoverableError.html" target="_blank" rel="noopener noreferrer">UnrecoverableError</a>, and <a href="https://docs.bullmq.io/api/classes/v6.Worker.html#canceljob" target="_blank" rel="noopener noreferrer">Worker.cancelJob</a>.</p>
|
|
364
560
|
|
|
365
561
|
<h3>Options</h3>
|
|
366
562
|
<ul>
|
|
@@ -379,6 +575,11 @@ return msg;</pre>
|
|
|
379
575
|
<li><code>getChildrenValues</code>: Reads completed child return values for a flow parent job.</li>
|
|
380
576
|
<li><code>getFailedChildrenValues</code>: Reads failed child values for a flow parent job.</li>
|
|
381
577
|
<li><code>removeUnprocessedChildren</code>: Removes children that have not yet been processed.</li>
|
|
578
|
+
<li><code>cancelJob</code>: Cancels the active job by aborting its processor signal, which fails the job and lets the queue's normal attempts/backoff retry policy apply. Acknowledgement-scoped: it needs <code>msg.bull.ackId</code>, so it only reaches manual-completion jobs that have not yet settled. <code>msg.reason</code> sets the cancellation reason, default <code>"BullMQ job cancelled"</code>. Outputs <code>true</code> on <code>msg.payload</code>; if BullMQ reports no cancellable processor for the job, the node raises an error instead.</li>
|
|
579
|
+
<li><code>cancelAllJobs</code>: Cancels every active manual job on the same <code>bullmq run</code> node the same way as <code>cancelJob</code>. <code>msg.reason</code> sets the cancellation reason, default <code>"BullMQ job cancelled"</code>. Outputs <code>true</code> on <code>msg.payload</code>.</li>
|
|
580
|
+
<li><code>moveToWait</code>: Puts the active job back in the waiting state to be picked up again, without counting a failed attempt. Terminal for this acknowledgement.</li>
|
|
581
|
+
<li><code>moveToDelayed</code>: Delays the active job by <code>msg.delay</code> milliseconds and resumes it later, without counting a failed attempt. Terminal for this acknowledgement. Combine with <code>updateData</code> to resume from a recorded step.</li>
|
|
582
|
+
<li><code>updateData</code>: Replaces the job's stored data with <code>msg.jobData</code>, or <code>msg.payload</code> when that is absent, and survives a retry. Non-terminal, so the flow still has to settle the acknowledgement. Use it to record which step a job reached before <code>moveToDelayed</code>.</li>
|
|
382
583
|
</ul>
|
|
383
584
|
|
|
384
585
|
<h3>Example</h3>
|
|
@@ -386,15 +587,19 @@ return msg;</pre>
|
|
|
386
587
|
<pre>msg.cmd = "progress";
|
|
387
588
|
msg.progress = 50;
|
|
388
589
|
return msg;</pre>
|
|
389
|
-
<p>
|
|
590
|
+
<p>To use another <code>bullmq job</code> node with configured <b>Action</b> <code>complete</code>, pass the progress output through a Function node that removes the per-message override:</p>
|
|
591
|
+
<pre>delete msg.cmd;
|
|
592
|
+
msg.payload = { ok: true };
|
|
593
|
+
return msg;</pre>
|
|
594
|
+
<p>Alternatively, override the downstream action explicitly:</p>
|
|
390
595
|
<pre>msg.cmd = "complete";
|
|
391
596
|
msg.payload = { ok: true };
|
|
392
597
|
return msg;</pre>
|
|
393
|
-
<p>The node uses the opaque <code>msg.bull.ackId</code> from <code>
|
|
598
|
+
<p>The node uses the opaque <code>msg.bull.ackId</code> from <code>bullmq run</code> and does not expose BullMQ lock tokens.</p>
|
|
394
599
|
</script>
|
|
395
600
|
|
|
396
601
|
<script type="text/javascript">
|
|
397
|
-
RED.nodes.registerType("
|
|
602
|
+
RED.nodes.registerType("bullmq job", {
|
|
398
603
|
color: "#ffffff",
|
|
399
604
|
category: "function",
|
|
400
605
|
defaults: {
|
|
@@ -406,12 +611,12 @@ return msg;</pre>
|
|
|
406
611
|
align: "right",
|
|
407
612
|
icon: "bull_icon.png",
|
|
408
613
|
label: function() {
|
|
409
|
-
return this.name || "
|
|
614
|
+
return this.name || "bullmq job";
|
|
410
615
|
}
|
|
411
616
|
});
|
|
412
617
|
</script>
|
|
413
618
|
|
|
414
|
-
<script type="text/html" data-template-name="
|
|
619
|
+
<script type="text/html" data-template-name="bullmq events">
|
|
415
620
|
<div class="form-row">
|
|
416
621
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
417
622
|
<input type="text" id="node-input-name">
|
|
@@ -426,16 +631,17 @@ return msg;</pre>
|
|
|
426
631
|
</div>
|
|
427
632
|
</script>
|
|
428
633
|
|
|
429
|
-
<script type="text/html" data-help-name="
|
|
634
|
+
<script type="text/html" data-help-name="bullmq events">
|
|
430
635
|
<p>Subscribes to BullMQ QueueEvents and emits global queue events as Node-RED messages.</p>
|
|
431
|
-
<p>API reference: <a href="https://
|
|
636
|
+
<p>API reference: <a href="https://docs.bullmq.io/api/classes/v6.QueueEvents.html" target="_blank" rel="noopener noreferrer">QueueEvents</a>.</p>
|
|
432
637
|
|
|
433
638
|
<h3>Options</h3>
|
|
434
639
|
<ul>
|
|
435
640
|
<li><b>Name</b>: Optional label shown in the Node-RED workspace.</li>
|
|
436
641
|
<li><b>Queue</b>: Queue config node whose global events will be observed.</li>
|
|
437
|
-
<li><b>Events</b>: Comma or newline separated event names. Leave empty to subscribe to the default set. Useful events include <code>completed</code>, <code>failed</code>, <code>progress</code>, <code>delayed</code>, <code>deduplicated</code>, <code>duplicated</code>, <code>waiting</code>, and <code>
|
|
642
|
+
<li><b>Events</b>: Comma or newline separated event names. Leave empty to subscribe to the default set. Useful events include <code>completed</code>, <code>failed</code>, <code>progress</code>, <code>delayed</code>, <code>deduplicated</code>, <code>duplicated</code>, <code>waiting</code>, <code>active</code>, and <code>retries-exhausted</code>.</li>
|
|
438
643
|
</ul>
|
|
644
|
+
<p><code>retries-exhausted</code> is part of the default event set. A flow that leaves <b>Events</b> empty now receives this event in addition to the previous defaults.</p>
|
|
439
645
|
|
|
440
646
|
<h3>Output message</h3>
|
|
441
647
|
<ul>
|
|
@@ -449,12 +655,12 @@ return msg;</pre>
|
|
|
449
655
|
</script>
|
|
450
656
|
|
|
451
657
|
<script type="text/javascript">
|
|
452
|
-
RED.nodes.registerType("
|
|
658
|
+
RED.nodes.registerType("bullmq events", {
|
|
453
659
|
color: "#ffffff",
|
|
454
660
|
category: "function",
|
|
455
661
|
defaults: {
|
|
456
662
|
name: { value: "" },
|
|
457
|
-
queue: { type: "
|
|
663
|
+
queue: { type: "bullmq-queue-server", required: true },
|
|
458
664
|
events: { value: "" }
|
|
459
665
|
},
|
|
460
666
|
inputs: 0,
|
|
@@ -462,12 +668,12 @@ return msg;</pre>
|
|
|
462
668
|
align: "left",
|
|
463
669
|
icon: "bull_icon.png",
|
|
464
670
|
label: function() {
|
|
465
|
-
return this.name || "
|
|
671
|
+
return this.name || "bullmq events";
|
|
466
672
|
}
|
|
467
673
|
});
|
|
468
674
|
</script>
|
|
469
675
|
|
|
470
|
-
<script type="text/html" data-template-name="
|
|
676
|
+
<script type="text/html" data-template-name="bullmq flow">
|
|
471
677
|
<div class="form-row">
|
|
472
678
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
473
679
|
<input type="text" id="node-input-name">
|
|
@@ -478,9 +684,9 @@ return msg;</pre>
|
|
|
478
684
|
</div>
|
|
479
685
|
</script>
|
|
480
686
|
|
|
481
|
-
<script type="text/html" data-help-name="
|
|
687
|
+
<script type="text/html" data-help-name="bullmq flow">
|
|
482
688
|
<p>Adds atomic BullMQ parent and child job trees with <code>FlowProducer</code>.</p>
|
|
483
|
-
<p>API reference: <a href="https://
|
|
689
|
+
<p>API reference: <a href="https://docs.bullmq.io/api/classes/v6.FlowProducer.html" target="_blank" rel="noopener noreferrer">FlowProducer</a> and <a href="https://docs.bullmq.io/api/types/v6.FlowJob.html" target="_blank" rel="noopener noreferrer">FlowJob</a>.</p>
|
|
484
690
|
|
|
485
691
|
<h3>Options</h3>
|
|
486
692
|
<ul>
|
|
@@ -490,10 +696,13 @@ return msg;</pre>
|
|
|
490
696
|
|
|
491
697
|
<h3>Input message</h3>
|
|
492
698
|
<ul>
|
|
493
|
-
<li><code>msg.payload</code>: BullMQ flow tree.
|
|
494
|
-
<li><code>msg.flowopts</code>: Optional FlowProducer options applied to
|
|
699
|
+
<li><code>msg.payload</code>: One BullMQ flow tree, or an array of independent root trees added atomically with <code>FlowProducer.addBulk</code>. A root object contains <code>name</code>, <code>queueName</code>, <code>data</code>, optional <code>opts</code>, and optional <code>children</code>.</li>
|
|
700
|
+
<li><code>msg.flowopts</code>: Optional FlowProducer options applied when adding one tree. It does not apply to an array.</li>
|
|
495
701
|
</ul>
|
|
496
702
|
|
|
703
|
+
<p>A child job without an explicit <code>opts.jobId</code> gets a UUID from BullMQ v6, not the incremental numeric id used before.</p>
|
|
704
|
+
<p>Use one tree for related parent/child jobs, including dependencies that span queues. Use an array only for independent root trees that must all be added or none added. On Redis Cluster, omit each flow job's <code>prefix</code> to use this node's prefix throughout. If prefixes are set per job, they must contain the same hash tag, and each worker must use its queue's exact prefix.</p>
|
|
705
|
+
|
|
497
706
|
<h3>Example</h3>
|
|
498
707
|
<pre>msg.payload = {
|
|
499
708
|
name: "parent-report",
|
|
@@ -508,23 +717,23 @@ return msg;</pre>
|
|
|
508
717
|
]
|
|
509
718
|
};
|
|
510
719
|
return msg;</pre>
|
|
511
|
-
<p>The child jobs are processed before the parent job. A parent can read child results with a manual-mode <code>
|
|
720
|
+
<p>The child jobs are processed before the parent job. A parent can read child results with a manual-mode <code>bullmq run</code> and a <code>bullmq job</code> action such as <code>getChildrenValues</code>.</p>
|
|
512
721
|
</script>
|
|
513
722
|
|
|
514
723
|
<script type="text/javascript">
|
|
515
|
-
RED.nodes.registerType("
|
|
724
|
+
RED.nodes.registerType("bullmq flow", {
|
|
516
725
|
color: "#ffffff",
|
|
517
726
|
category: "function",
|
|
518
727
|
defaults: {
|
|
519
728
|
name: { value: "" },
|
|
520
|
-
queue: { type: "
|
|
729
|
+
queue: { type: "bullmq-queue-server", required: true }
|
|
521
730
|
},
|
|
522
731
|
inputs: 1,
|
|
523
732
|
outputs: 1,
|
|
524
733
|
align: "right",
|
|
525
734
|
icon: "bull_icon.png",
|
|
526
735
|
label: function() {
|
|
527
|
-
return this.name || "
|
|
736
|
+
return this.name || "bullmq flow";
|
|
528
737
|
}
|
|
529
738
|
});
|
|
530
739
|
</script>
|