@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/bull-queue.html CHANGED
@@ -1,9 +1,16 @@
1
- <script type="text/html" data-template-name="bull-queue-server">
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="basecasts">
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="default">
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 Redis data connections</span>
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="redis.example.com">
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-single-row").toggle(deployment === "single");
96
- $(".bull-cluster-row").toggle(deployment === "cluster");
97
- $(".bull-sentinel-row").toggle(deployment === "sentinel");
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("bull-queue-server", {
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="bull-queue-server">
140
- <p>Configures the BullMQ queue name and Redis connection shared by the BullMQ nodes.</p>
141
- <p>API reference: <a href="https://api.docs.bullmq.io/classes/v5.Queue.html" target="_blank" rel="noopener noreferrer">Queue</a> and <a href="https://api.docs.bullmq.io/interfaces/v5.QueueOptions.html" target="_blank" rel="noopener noreferrer">QueueOptions</a>.</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 default Redis port is <code>6379</code>.</li>
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>Username</b>: Redis ACL username for data connections. Leave empty for Redis servers that only use a password.</li>
154
- <li><b>Password</b>: Redis password for data connections. Stored as a Node-RED credential.</li>
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 &times; <b>Pool Max</b> + 2 server connections &mdash; 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 Redis data connections. Use this for AWS MemoryDB and any Redis deployment that requires encrypted data connections.</li>
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 Redis certificates. Stored as a Node-RED credential.</li>
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> so BullMQ keys stay in one hash slot for atomic operations.</li>
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 should use <code>maxmemory-policy=noeviction</code>. Bull v4 Redis queue data is not automatically migrated to BullMQ.</p>
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="bull cmd">
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="bull cmd">
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://api.docs.bullmq.io/classes/v5.Queue.html" target="_blank" rel="noopener noreferrer">Queue</a>, <a href="https://api.docs.bullmq.io/classes/v5.Queue.html#add" target="_blank" rel="noopener noreferrer">Queue.add</a>, <a href="https://api.docs.bullmq.io/types/v5.JobsOptions.html" target="_blank" rel="noopener noreferrer">JobsOptions</a>, <a href="https://api.docs.bullmq.io/types/v5.DeduplicationOptions.html" target="_blank" rel="noopener noreferrer">DeduplicationOptions</a>, <a href="https://api.docs.bullmq.io/classes/v5.Queue.html#setglobalratelimit" target="_blank" rel="noopener noreferrer">Queue.setGlobalRateLimit</a>, and <a href="https://api.docs.bullmq.io/classes/v5.Queue.html#upsertjobscheduler" target="_blank" rel="noopener noreferrer">Queue.upsertJobScheduler</a>.</p>
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>removeJobScheduler</code>.</li>
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>.</li>
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>, and <code>repeat</code>.</li>
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 = "add";
228
- msg.jobName = "heartbeat";
229
- msg.payload = "scheduled heartbeat";
230
- msg.jobopts = {
231
- jobId: "heartbeat-every-minute",
232
- repeat: { pattern: "*/1 * * * *" }
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>Legacy <code>msg.jobopts.repeat.cron</code> is translated to a BullMQ Job Scheduler. Legacy repeat lookup and removal commands use exact scheduler ids.</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("bull cmd", {
385
+ RED.nodes.registerType("bullmq cmd", {
240
386
  color: "#ffffff",
241
387
  category: "function",
242
388
  defaults: {
243
389
  name: { value: "" },
244
- queue: { type: "bull-queue-server", required: true }
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 || "bull cmd";
397
+ return this.name || "bullmq cmd";
252
398
  }
253
399
  });
254
400
  </script>
255
401
 
256
- <script type="text/html" data-template-name="bull run">
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="bull run">
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://api.docs.bullmq.io/classes/v5.Worker.html" target="_blank" rel="noopener noreferrer">Worker</a> and <a href="https://api.docs.bullmq.io/interfaces/v5.WorkerOptions.html" target="_blank" rel="noopener noreferrer">WorkerOptions</a>.</p>
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>bull job</code> node completes, fails, or updates it.</li>
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>bull job</code> nodes.</li>
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>bull job</code> with <b>Action</b> <code>complete</code> or <code>fail</code>.</p>
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
- RED.nodes.registerType("bull run", {
319
- color: "#ffffff",
320
- category: "function",
321
- defaults: {
322
- name: { value: "" },
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="bull job">
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="bull job">
362
- <p>Acts on an active job emitted by a manual-mode <code>bull run</code> node.</p>
363
- <p>API reference: <a href="https://api.docs.bullmq.io/classes/v5.Job.html" target="_blank" rel="noopener noreferrer">Job</a> and <a href="https://api.docs.bullmq.io/classes/v5.UnrecoverableError.html" target="_blank" rel="noopener noreferrer">UnrecoverableError</a>.</p>
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>Wire that message into another <code>bull job</code> node with <b>Action</b> <code>complete</code>, or set:</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>bull run</code> and does not expose BullMQ lock tokens.</p>
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("bull job", {
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 || "bull job";
614
+ return this.name || "bullmq job";
410
615
  }
411
616
  });
412
617
  </script>
413
618
 
414
- <script type="text/html" data-template-name="bull events">
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="bull events">
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://api.docs.bullmq.io/classes/v5.QueueEvents.html" target="_blank" rel="noopener noreferrer">QueueEvents</a>.</p>
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>active</code>.</li>
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("bull events", {
658
+ RED.nodes.registerType("bullmq events", {
453
659
  color: "#ffffff",
454
660
  category: "function",
455
661
  defaults: {
456
662
  name: { value: "" },
457
- queue: { type: "bull-queue-server", required: true },
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 || "bull events";
671
+ return this.name || "bullmq events";
466
672
  }
467
673
  });
468
674
  </script>
469
675
 
470
- <script type="text/html" data-template-name="bull flow">
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="bull flow">
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://api.docs.bullmq.io/classes/v5.FlowProducer.html" target="_blank" rel="noopener noreferrer">FlowProducer</a> and <a href="https://api.docs.bullmq.io/types/v5.FlowJob.html" target="_blank" rel="noopener noreferrer">FlowJob</a>.</p>
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. The root object contains <code>name</code>, <code>queueName</code>, <code>data</code>, optional <code>opts</code>, and optional <code>children</code>.</li>
494
- <li><code>msg.flowopts</code>: Optional FlowProducer options applied to the add call.</li>
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>bull run</code> and a <code>bull job</code> action such as <code>getChildrenValues</code>.</p>
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("bull flow", {
724
+ RED.nodes.registerType("bullmq flow", {
516
725
  color: "#ffffff",
517
726
  category: "function",
518
727
  defaults: {
519
728
  name: { value: "" },
520
- queue: { type: "bull-queue-server", required: true }
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 || "bull flow";
736
+ return this.name || "bullmq flow";
528
737
  }
529
738
  });
530
739
  </script>