@pauldeng/node-red-contrib-bullmq 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +124 -0
- package/bull-queue.html +530 -0
- package/bull-queue.js +523 -0
- package/docs/ARCHITECTURE.md +52 -0
- package/docs/CHANGE_WORKFLOW.md +27 -0
- package/docs/COMMANDS.md +89 -0
- package/docs/CONNECTIONS.md +54 -0
- package/docs/MIGRATION.md +37 -0
- package/docs/NODE_GUIDE.md +83 -0
- package/docs/REFERENCE_MAP.md +51 -0
- package/docs/RELEASE.md +108 -0
- package/docs/TESTING.md +88 -0
- package/docs/TROUBLESHOOTING.md +28 -0
- package/examples/README.md +35 -0
- package/examples/bullmq_features.json +331 -0
- package/examples/example_flow.json +277 -0
- package/examples/repeatable_jobs.json +245 -0
- package/icons/bull_icon.png +0 -0
- package/lib/acknowledgements.js +110 -0
- package/lib/commands.js +230 -0
- package/lib/connections.js +300 -0
- package/lib/scheduler.js +114 -0
- package/lib/serialization.js +63 -0
- package/package.json +70 -0
package/bull-queue.html
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
<script type="text/html" data-template-name="bull-queue-server">
|
|
2
|
+
<div class="form-row">
|
|
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">
|
|
5
|
+
</div>
|
|
6
|
+
<div class="form-row">
|
|
7
|
+
<label for="node-config-input-deployment"><i class="fa fa-server"></i> Deployment</label>
|
|
8
|
+
<select id="node-config-input-deployment" style="width: 70%;">
|
|
9
|
+
<option value="single">Standalone Redis</option>
|
|
10
|
+
<option value="cluster">Redis Cluster / AWS MemoryDB</option>
|
|
11
|
+
<option value="sentinel">Redis Sentinel</option>
|
|
12
|
+
</select>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="form-row bull-single-row">
|
|
15
|
+
<label for="node-config-input-address"><i class="fa fa-link"></i> Host</label>
|
|
16
|
+
<input type="text" id="node-config-input-address" placeholder="localhost">
|
|
17
|
+
</div>
|
|
18
|
+
<div class="form-row bull-single-row">
|
|
19
|
+
<label for="node-config-input-port"><i class="fa fa-plug"></i> Port</label>
|
|
20
|
+
<input type="number" id="node-config-input-port" placeholder="6379">
|
|
21
|
+
</div>
|
|
22
|
+
<div class="form-row bull-cluster-row">
|
|
23
|
+
<label for="node-config-input-clusterNodes"><i class="fa fa-sitemap"></i> Cluster Nodes</label>
|
|
24
|
+
<textarea id="node-config-input-clusterNodes" rows="3" style="width: 70%;" placeholder="host-a:6379, host-b:6379"></textarea>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="form-row bull-sentinel-row">
|
|
27
|
+
<label for="node-config-input-sentinels"><i class="fa fa-sitemap"></i> Sentinels</label>
|
|
28
|
+
<textarea id="node-config-input-sentinels" rows="3" style="width: 70%;" placeholder="sentinel-a:26379, sentinel-b:26379"></textarea>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="form-row bull-sentinel-row">
|
|
31
|
+
<label for="node-config-input-sentinelMasterName"><i class="fa fa-tag"></i> Master</label>
|
|
32
|
+
<input type="text" id="node-config-input-sentinelMasterName" placeholder="mymaster">
|
|
33
|
+
</div>
|
|
34
|
+
<div class="form-row">
|
|
35
|
+
<label for="node-config-input-db"><i class="fa fa-database"></i> Database</label>
|
|
36
|
+
<input type="number" id="node-config-input-db" placeholder="0">
|
|
37
|
+
</div>
|
|
38
|
+
<div class="form-row">
|
|
39
|
+
<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">
|
|
41
|
+
</div>
|
|
42
|
+
<div class="form-row">
|
|
43
|
+
<label for="node-config-input-password"><i class="fa fa-lock"></i> Password</label>
|
|
44
|
+
<input type="password" id="node-config-input-password">
|
|
45
|
+
</div>
|
|
46
|
+
<div class="form-row bull-sentinel-row">
|
|
47
|
+
<label for="node-config-input-sentinelUsername"><i class="fa fa-user"></i> Sentinel User</label>
|
|
48
|
+
<input type="text" id="node-config-input-sentinelUsername">
|
|
49
|
+
</div>
|
|
50
|
+
<div class="form-row bull-sentinel-row">
|
|
51
|
+
<label for="node-config-input-sentinelPassword"><i class="fa fa-lock"></i> Sentinel Pass</label>
|
|
52
|
+
<input type="password" id="node-config-input-sentinelPassword">
|
|
53
|
+
</div>
|
|
54
|
+
<div class="form-row">
|
|
55
|
+
<label for="node-config-input-tls"><i class="fa fa-shield"></i> TLS</label>
|
|
56
|
+
<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>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="form-row bull-sentinel-row">
|
|
60
|
+
<label for="node-config-input-sentinelTls"><i class="fa fa-shield"></i> Sentinel TLS</label>
|
|
61
|
+
<input type="checkbox" id="node-config-input-sentinelTls" style="display:inline-block; width:auto; vertical-align:top;">
|
|
62
|
+
<span>Use TLS for Sentinel discovery</span>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="form-row bull-tls-row">
|
|
65
|
+
<label for="node-config-input-tlsRejectUnauthorized"><i class="fa fa-check"></i> Verify TLS</label>
|
|
66
|
+
<input type="checkbox" id="node-config-input-tlsRejectUnauthorized" style="display:inline-block; width:auto; vertical-align:top;">
|
|
67
|
+
<span>Reject unauthorized certificates</span>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="form-row bull-tls-row">
|
|
70
|
+
<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">
|
|
72
|
+
</div>
|
|
73
|
+
<div class="form-row bull-tls-row">
|
|
74
|
+
<label for="node-config-input-tlsCa"><i class="fa fa-certificate"></i> CA</label>
|
|
75
|
+
<textarea id="node-config-input-tlsCa" rows="3" style="width: 70%;"></textarea>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="form-row bull-tls-row">
|
|
78
|
+
<label for="node-config-input-tlsCert"><i class="fa fa-certificate"></i> Client Cert</label>
|
|
79
|
+
<textarea id="node-config-input-tlsCert" rows="3" style="width: 70%;"></textarea>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="form-row bull-tls-row">
|
|
82
|
+
<label for="node-config-input-tlsKey"><i class="fa fa-key"></i> Client Key</label>
|
|
83
|
+
<textarea id="node-config-input-tlsKey" rows="3" style="width: 70%;"></textarea>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="form-row">
|
|
86
|
+
<label for="node-config-input-prefix"><i class="fa fa-code"></i> Prefix</label>
|
|
87
|
+
<input type="text" id="node-config-input-prefix" placeholder="{bull}">
|
|
88
|
+
</div>
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<script type="text/javascript">
|
|
92
|
+
(function() {
|
|
93
|
+
function updateBullQueueServerRows() {
|
|
94
|
+
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");
|
|
98
|
+
$(".bull-tls-row").toggle($("#node-config-input-tls").is(":checked"));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
RED.nodes.registerType("bull-queue-server", {
|
|
102
|
+
category: "config",
|
|
103
|
+
defaults: {
|
|
104
|
+
name: { value: "", required: true },
|
|
105
|
+
deployment: { value: "single", required: true },
|
|
106
|
+
address: { value: "localhost" },
|
|
107
|
+
port: { value: 6379, validate: RED.validators.number() },
|
|
108
|
+
db: { value: "" },
|
|
109
|
+
clusterNodes: { value: "" },
|
|
110
|
+
sentinels: { value: "" },
|
|
111
|
+
sentinelMasterName: { value: "" },
|
|
112
|
+
username: { value: "" },
|
|
113
|
+
sentinelUsername: { value: "" },
|
|
114
|
+
tls: { value: false },
|
|
115
|
+
sentinelTls: { value: false },
|
|
116
|
+
tlsRejectUnauthorized: { value: true },
|
|
117
|
+
tlsServerName: { value: "" },
|
|
118
|
+
prefix: { value: "" }
|
|
119
|
+
},
|
|
120
|
+
credentials: {
|
|
121
|
+
password: { type: "password" },
|
|
122
|
+
sentinelPassword: { type: "password" },
|
|
123
|
+
tlsCa: { type: "password" },
|
|
124
|
+
tlsCert: { type: "password" },
|
|
125
|
+
tlsKey: { type: "password" }
|
|
126
|
+
},
|
|
127
|
+
label: function() {
|
|
128
|
+
return this.name || this.address + ":" + this.port;
|
|
129
|
+
},
|
|
130
|
+
oneditprepare: function() {
|
|
131
|
+
$("#node-config-input-deployment").on("change", updateBullQueueServerRows);
|
|
132
|
+
$("#node-config-input-tls").on("change", updateBullQueueServerRows);
|
|
133
|
+
updateBullQueueServerRows();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
})();
|
|
137
|
+
</script>
|
|
138
|
+
|
|
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>
|
|
142
|
+
|
|
143
|
+
<h3>Options</h3>
|
|
144
|
+
<ul>
|
|
145
|
+
<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>
|
|
146
|
+
<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>
|
|
149
|
+
<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
|
+
<li><b>Sentinels</b>: Comma or newline separated Sentinel endpoints such as <code>sentinel-a:26379, sentinel-b:26379</code>.</li>
|
|
151
|
+
<li><b>Master</b>: Sentinel master name, for example <code>mymaster</code>.</li>
|
|
152
|
+
<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>
|
|
155
|
+
<li><b>Sentinel User</b>: Redis ACL username for Sentinel discovery connections when Sentinels require separate credentials.</li>
|
|
156
|
+
<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>
|
|
158
|
+
<li><b>Sentinel TLS</b>: Enables TLS for Sentinel discovery connections.</li>
|
|
159
|
+
<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>
|
|
162
|
+
<li><b>Client Cert</b>: PEM client certificate for mutual TLS deployments. Stored as a Node-RED credential.</li>
|
|
163
|
+
<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. For Redis Cluster and MemoryDB, use a hash tag such as <code>{bull}</code> so BullMQ keys stay in one hash slot for atomic operations.</li>
|
|
165
|
+
</ul>
|
|
166
|
+
|
|
167
|
+
<h3>Example</h3>
|
|
168
|
+
<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>
|
|
169
|
+
<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>
|
|
171
|
+
</script>
|
|
172
|
+
|
|
173
|
+
<script type="text/html" data-template-name="bull cmd">
|
|
174
|
+
<div class="form-row">
|
|
175
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
176
|
+
<input type="text" id="node-input-name">
|
|
177
|
+
</div>
|
|
178
|
+
<div class="form-row">
|
|
179
|
+
<label for="node-input-queue"><i class="fa fa-tasks"></i> Queue</label>
|
|
180
|
+
<input type="text" id="node-input-queue">
|
|
181
|
+
</div>
|
|
182
|
+
</script>
|
|
183
|
+
|
|
184
|
+
<script type="text/html" data-help-name="bull cmd">
|
|
185
|
+
<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>
|
|
187
|
+
|
|
188
|
+
<h3>Options</h3>
|
|
189
|
+
<ul>
|
|
190
|
+
<li><b>Name</b>: Optional label shown in the Node-RED workspace.</li>
|
|
191
|
+
<li><b>Queue</b>: Queue config node used to create the BullMQ <code>Queue</code>.</li>
|
|
192
|
+
</ul>
|
|
193
|
+
|
|
194
|
+
<h3>Input message</h3>
|
|
195
|
+
<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>
|
|
198
|
+
<li><code>msg.jobName</code>: BullMQ job name for <code>add</code>. Defaults to <code>default</code>.</li>
|
|
199
|
+
<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>
|
|
201
|
+
</ul>
|
|
202
|
+
|
|
203
|
+
<h3>Examples</h3>
|
|
204
|
+
<p>Add a delayed, prioritized job:</p>
|
|
205
|
+
<pre>msg.cmd = "add";
|
|
206
|
+
msg.jobName = "email";
|
|
207
|
+
msg.payload = { to: "user@example.com" };
|
|
208
|
+
msg.jobopts = {
|
|
209
|
+
delay: 10000,
|
|
210
|
+
priority: 1,
|
|
211
|
+
removeOnComplete: true
|
|
212
|
+
};
|
|
213
|
+
return msg;</pre>
|
|
214
|
+
<p>Add one job per deduplication id:</p>
|
|
215
|
+
<pre>msg.cmd = "add";
|
|
216
|
+
msg.jobName = "report";
|
|
217
|
+
msg.payload = { customerId: "customer-42" };
|
|
218
|
+
msg.jobopts = {
|
|
219
|
+
deduplication: { id: "customer-42-report" }
|
|
220
|
+
};
|
|
221
|
+
return msg;</pre>
|
|
222
|
+
<p>Set a global rate limit of two jobs per second:</p>
|
|
223
|
+
<pre>msg.cmd = "setGlobalRateLimit";
|
|
224
|
+
msg.payload = { max: 2, duration: 1000 };
|
|
225
|
+
return msg;</pre>
|
|
226
|
+
<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 * * * *" }
|
|
233
|
+
};
|
|
234
|
+
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>
|
|
236
|
+
</script>
|
|
237
|
+
|
|
238
|
+
<script type="text/javascript">
|
|
239
|
+
RED.nodes.registerType("bull cmd", {
|
|
240
|
+
color: "#ffffff",
|
|
241
|
+
category: "function",
|
|
242
|
+
defaults: {
|
|
243
|
+
name: { value: "" },
|
|
244
|
+
queue: { type: "bull-queue-server", required: true }
|
|
245
|
+
},
|
|
246
|
+
inputs: 1,
|
|
247
|
+
outputs: 1,
|
|
248
|
+
align: "right",
|
|
249
|
+
icon: "bull_icon.png",
|
|
250
|
+
label: function() {
|
|
251
|
+
return this.name || "bull cmd";
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
</script>
|
|
255
|
+
|
|
256
|
+
<script type="text/html" data-template-name="bull run">
|
|
257
|
+
<div class="form-row">
|
|
258
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
259
|
+
<input type="text" id="node-input-name">
|
|
260
|
+
</div>
|
|
261
|
+
<div class="form-row">
|
|
262
|
+
<label for="node-input-queue"><i class="fa fa-tasks"></i> Queue</label>
|
|
263
|
+
<input type="text" id="node-input-queue">
|
|
264
|
+
</div>
|
|
265
|
+
<div class="form-row">
|
|
266
|
+
<label for="node-input-completionMode"><i class="fa fa-check-circle"></i> Completion</label>
|
|
267
|
+
<select id="node-input-completionMode" style="width: 70%;">
|
|
268
|
+
<option value="immediate">Immediate</option>
|
|
269
|
+
<option value="manual">Manual acknowledgement</option>
|
|
270
|
+
</select>
|
|
271
|
+
</div>
|
|
272
|
+
<div class="form-row">
|
|
273
|
+
<label for="node-input-ackTimeout"><i class="fa fa-clock-o"></i> Ack Timeout</label>
|
|
274
|
+
<input type="number" id="node-input-ackTimeout" placeholder="300000">
|
|
275
|
+
</div>
|
|
276
|
+
<div class="form-row">
|
|
277
|
+
<label for="node-input-concurrency"><i class="fa fa-random"></i> Concurrency</label>
|
|
278
|
+
<input type="number" id="node-input-concurrency" placeholder="1">
|
|
279
|
+
</div>
|
|
280
|
+
<div class="form-row">
|
|
281
|
+
<label for="node-input-limiterMax"><i class="fa fa-tachometer"></i> Limiter Max</label>
|
|
282
|
+
<input type="number" id="node-input-limiterMax">
|
|
283
|
+
</div>
|
|
284
|
+
<div class="form-row">
|
|
285
|
+
<label for="node-input-limiterDuration"><i class="fa fa-hourglass-half"></i> Limiter Duration</label>
|
|
286
|
+
<input type="number" id="node-input-limiterDuration">
|
|
287
|
+
</div>
|
|
288
|
+
</script>
|
|
289
|
+
|
|
290
|
+
<script type="text/html" data-help-name="bull run">
|
|
291
|
+
<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>
|
|
293
|
+
|
|
294
|
+
<h3>Options</h3>
|
|
295
|
+
<ul>
|
|
296
|
+
<li><b>Name</b>: Optional label shown in the Node-RED workspace.</li>
|
|
297
|
+
<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>
|
|
299
|
+
<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
|
+
<li><b>Concurrency</b>: Number of jobs this worker can process at the same time.</li>
|
|
301
|
+
<li><b>Limiter Max</b>: Optional worker-side rate limit count.</li>
|
|
302
|
+
<li><b>Limiter Duration</b>: Optional worker-side rate limit window in milliseconds. Set with <b>Limiter Max</b>.</li>
|
|
303
|
+
</ul>
|
|
304
|
+
|
|
305
|
+
<h3>Output message</h3>
|
|
306
|
+
<ul>
|
|
307
|
+
<li><code>msg.payload</code>: <code>job.data.payload</code> when present, otherwise the full BullMQ job data.</li>
|
|
308
|
+
<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>
|
|
310
|
+
</ul>
|
|
311
|
+
|
|
312
|
+
<h3>Example</h3>
|
|
313
|
+
<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>
|
|
315
|
+
</script>
|
|
316
|
+
|
|
317
|
+
<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";
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
</script>
|
|
339
|
+
|
|
340
|
+
<script type="text/html" data-template-name="bull job">
|
|
341
|
+
<div class="form-row">
|
|
342
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
343
|
+
<input type="text" id="node-input-name">
|
|
344
|
+
</div>
|
|
345
|
+
<div class="form-row">
|
|
346
|
+
<label for="node-input-action"><i class="fa fa-check"></i> Action</label>
|
|
347
|
+
<select id="node-input-action" style="width: 70%;">
|
|
348
|
+
<option value="complete">Complete</option>
|
|
349
|
+
<option value="fail">Fail</option>
|
|
350
|
+
<option value="failUnrecoverable">Fail unrecoverable</option>
|
|
351
|
+
<option value="progress">Progress</option>
|
|
352
|
+
<option value="rateLimit">Rate limit</option>
|
|
353
|
+
<option value="removeDeduplicationKey">Remove deduplication key</option>
|
|
354
|
+
<option value="getChildrenValues">Get children values</option>
|
|
355
|
+
<option value="getFailedChildrenValues">Get failed children values</option>
|
|
356
|
+
<option value="removeUnprocessedChildren">Remove unprocessed children</option>
|
|
357
|
+
</select>
|
|
358
|
+
</div>
|
|
359
|
+
</script>
|
|
360
|
+
|
|
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>
|
|
364
|
+
|
|
365
|
+
<h3>Options</h3>
|
|
366
|
+
<ul>
|
|
367
|
+
<li><b>Name</b>: Optional label shown in the Node-RED workspace.</li>
|
|
368
|
+
<li><b>Action</b>: Default job action. It can be overridden per message with <code>msg.cmd</code>.</li>
|
|
369
|
+
</ul>
|
|
370
|
+
|
|
371
|
+
<h3>Actions</h3>
|
|
372
|
+
<ul>
|
|
373
|
+
<li><code>complete</code>: Completes the active job. Use <code>msg.payload</code> as the return value.</li>
|
|
374
|
+
<li><code>fail</code>: Fails the active job. Use <code>msg.error</code> or <code>msg.payload</code> to describe the error.</li>
|
|
375
|
+
<li><code>failUnrecoverable</code>: Fails the active job with a BullMQ unrecoverable error so retry attempts are not used.</li>
|
|
376
|
+
<li><code>progress</code>: Updates job progress from <code>msg.progress</code> or <code>msg.payload</code>.</li>
|
|
377
|
+
<li><code>rateLimit</code>: Applies a worker rate limit delay. Use <code>msg.duration</code> as milliseconds.</li>
|
|
378
|
+
<li><code>removeDeduplicationKey</code>: Removes the active job deduplication key.</li>
|
|
379
|
+
<li><code>getChildrenValues</code>: Reads completed child return values for a flow parent job.</li>
|
|
380
|
+
<li><code>getFailedChildrenValues</code>: Reads failed child values for a flow parent job.</li>
|
|
381
|
+
<li><code>removeUnprocessedChildren</code>: Removes children that have not yet been processed.</li>
|
|
382
|
+
</ul>
|
|
383
|
+
|
|
384
|
+
<h3>Example</h3>
|
|
385
|
+
<p>Update progress, then complete a manual job:</p>
|
|
386
|
+
<pre>msg.cmd = "progress";
|
|
387
|
+
msg.progress = 50;
|
|
388
|
+
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>
|
|
390
|
+
<pre>msg.cmd = "complete";
|
|
391
|
+
msg.payload = { ok: true };
|
|
392
|
+
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>
|
|
394
|
+
</script>
|
|
395
|
+
|
|
396
|
+
<script type="text/javascript">
|
|
397
|
+
RED.nodes.registerType("bull job", {
|
|
398
|
+
color: "#ffffff",
|
|
399
|
+
category: "function",
|
|
400
|
+
defaults: {
|
|
401
|
+
name: { value: "" },
|
|
402
|
+
action: { value: "complete" }
|
|
403
|
+
},
|
|
404
|
+
inputs: 1,
|
|
405
|
+
outputs: 1,
|
|
406
|
+
align: "right",
|
|
407
|
+
icon: "bull_icon.png",
|
|
408
|
+
label: function() {
|
|
409
|
+
return this.name || "bull job";
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
</script>
|
|
413
|
+
|
|
414
|
+
<script type="text/html" data-template-name="bull events">
|
|
415
|
+
<div class="form-row">
|
|
416
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
417
|
+
<input type="text" id="node-input-name">
|
|
418
|
+
</div>
|
|
419
|
+
<div class="form-row">
|
|
420
|
+
<label for="node-input-queue"><i class="fa fa-tasks"></i> Queue</label>
|
|
421
|
+
<input type="text" id="node-input-queue">
|
|
422
|
+
</div>
|
|
423
|
+
<div class="form-row">
|
|
424
|
+
<label for="node-input-events"><i class="fa fa-bell"></i> Events</label>
|
|
425
|
+
<textarea id="node-input-events" rows="4" style="width: 70%;" placeholder="completed, failed, progress"></textarea>
|
|
426
|
+
</div>
|
|
427
|
+
</script>
|
|
428
|
+
|
|
429
|
+
<script type="text/html" data-help-name="bull events">
|
|
430
|
+
<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>
|
|
432
|
+
|
|
433
|
+
<h3>Options</h3>
|
|
434
|
+
<ul>
|
|
435
|
+
<li><b>Name</b>: Optional label shown in the Node-RED workspace.</li>
|
|
436
|
+
<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>
|
|
438
|
+
</ul>
|
|
439
|
+
|
|
440
|
+
<h3>Output message</h3>
|
|
441
|
+
<ul>
|
|
442
|
+
<li><code>msg.topic</code>: Event name.</li>
|
|
443
|
+
<li><code>msg.payload</code>: BullMQ event payload.</li>
|
|
444
|
+
<li><code>msg.bull</code>: Queue, event name, and event id metadata.</li>
|
|
445
|
+
</ul>
|
|
446
|
+
|
|
447
|
+
<h3>Example</h3>
|
|
448
|
+
<p>Set <b>Events</b> to <code>completed, failed, delayed, deduplicated, progress</code> and wire the node to a debug node to inspect queue activity.</p>
|
|
449
|
+
</script>
|
|
450
|
+
|
|
451
|
+
<script type="text/javascript">
|
|
452
|
+
RED.nodes.registerType("bull events", {
|
|
453
|
+
color: "#ffffff",
|
|
454
|
+
category: "function",
|
|
455
|
+
defaults: {
|
|
456
|
+
name: { value: "" },
|
|
457
|
+
queue: { type: "bull-queue-server", required: true },
|
|
458
|
+
events: { value: "" }
|
|
459
|
+
},
|
|
460
|
+
inputs: 0,
|
|
461
|
+
outputs: 1,
|
|
462
|
+
align: "left",
|
|
463
|
+
icon: "bull_icon.png",
|
|
464
|
+
label: function() {
|
|
465
|
+
return this.name || "bull events";
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
</script>
|
|
469
|
+
|
|
470
|
+
<script type="text/html" data-template-name="bull flow">
|
|
471
|
+
<div class="form-row">
|
|
472
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
473
|
+
<input type="text" id="node-input-name">
|
|
474
|
+
</div>
|
|
475
|
+
<div class="form-row">
|
|
476
|
+
<label for="node-input-queue"><i class="fa fa-tasks"></i> Queue</label>
|
|
477
|
+
<input type="text" id="node-input-queue">
|
|
478
|
+
</div>
|
|
479
|
+
</script>
|
|
480
|
+
|
|
481
|
+
<script type="text/html" data-help-name="bull flow">
|
|
482
|
+
<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>
|
|
484
|
+
|
|
485
|
+
<h3>Options</h3>
|
|
486
|
+
<ul>
|
|
487
|
+
<li><b>Name</b>: Optional label shown in the Node-RED workspace.</li>
|
|
488
|
+
<li><b>Queue</b>: Queue config node used by the FlowProducer connection. Each flow job still supplies its own <code>queueName</code>.</li>
|
|
489
|
+
</ul>
|
|
490
|
+
|
|
491
|
+
<h3>Input message</h3>
|
|
492
|
+
<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>
|
|
495
|
+
</ul>
|
|
496
|
+
|
|
497
|
+
<h3>Example</h3>
|
|
498
|
+
<pre>msg.payload = {
|
|
499
|
+
name: "parent-report",
|
|
500
|
+
queueName: "reports",
|
|
501
|
+
data: { payload: "parent" },
|
|
502
|
+
children: [
|
|
503
|
+
{
|
|
504
|
+
name: "child-report",
|
|
505
|
+
queueName: "reports",
|
|
506
|
+
data: { payload: "child" }
|
|
507
|
+
}
|
|
508
|
+
]
|
|
509
|
+
};
|
|
510
|
+
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>
|
|
512
|
+
</script>
|
|
513
|
+
|
|
514
|
+
<script type="text/javascript">
|
|
515
|
+
RED.nodes.registerType("bull flow", {
|
|
516
|
+
color: "#ffffff",
|
|
517
|
+
category: "function",
|
|
518
|
+
defaults: {
|
|
519
|
+
name: { value: "" },
|
|
520
|
+
queue: { type: "bull-queue-server", required: true }
|
|
521
|
+
},
|
|
522
|
+
inputs: 1,
|
|
523
|
+
outputs: 1,
|
|
524
|
+
align: "right",
|
|
525
|
+
icon: "bull_icon.png",
|
|
526
|
+
label: function() {
|
|
527
|
+
return this.name || "bull flow";
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
</script>
|