UNPKG

19.4 kBMarkdownView Raw
1
2<div align="center">
3 <br/>
4 <img src="./support/logo@2x.png" width="300" />
5 <br/>
6 <br/>
7 <p>
8 The fastest, most reliable, Redis-based queue for Node. <br/>
9 Carefully written for rock solid stability and atomicity.
10 </p>
11 <br/>
12 <p>
13 <a href="#-sponsors-"><strong>Sponsors</strong></a> ·
14 <a href="#bull-features"><strong>Features</strong></a> ·
15 <a href="#uis"><strong>UIs</strong></a> ·
16 <a href="#install"><strong>Install</strong></a> ·
17 <a href="#quick-guide"><strong>Quick Guide</strong></a> ·
18 <a href="#documentation"><strong>Documentation</strong></a>
19 </p>
20 <p>Check the new <a href="https://optimalbits.github.io/bull/"><strong>Guide!</strong></p>
21 <br/>
22 <p>
23 <a href="https://gitter.im/OptimalBits/bull">
24 <img src="https://badges.gitter.im/Join%20Chat.svg"/>
25 </a>
26 <a href="https://gitter.im/OptimalBits/bull">
27 <img src="https://img.shields.io/npm/dm/bull.svg?maxAge=2592000"/>
28 </a>
29 <a href="http://badge.fury.io/js/bull">
30 <img src="https://badge.fury.io/js/bull.svg"/>
31 </a>
32 <a href="https://coveralls.io/github/OptimalBits/bull?branch=master">
33 <img src="https://coveralls.io/repos/github/OptimalBits/bull/badge.svg?branch=master"/>
34 </a>
35 <a href="http://isitmaintained.com/project/OptimalBits/bull">
36 <img src="http://isitmaintained.com/badge/open/optimalbits/bull.svg"/>
37 </a>
38 <a href="http://isitmaintained.com/project/OptimalBits/bull">
39 <img src="http://isitmaintained.com/badge/resolution/optimalbits/bull.svg"/>
40 </a>
41 <a href="https://twitter.com/manast">
42 <img src="https://img.shields.io/twitter/follow/manast?label=Stay%20updated&style=social"/>
43 </a>
44 </p>
45</div>
46
47### 📻 News and updates
48
49Bull is currently in maintenance mode, we are only fixing bugs. For new features check [BullMQ](https://github.com/taskforcesh/bullmq), a modern rewritten
50implementation in Typescript. You are still very welcome to use Bull if it suits your needs, which is a safe, battle tested library.
51
52Follow me on [Twitter](http://twitter.com/manast) for other important news and updates.
53
54### 🛠 Tutorials
55
56You can find tutorials and news in this blog: https://blog.taskforce.sh/
57
58---
59
60### Used by
61
62Bull is popular among large and small organizations, like the following ones:
63
64<table cellspacing="0" cellpadding="0">
65 <tr>
66 <td valign="center">
67 <a href="https://github.com/atlassian/github-for-jira">
68 <img
69 src="https://876297641-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LUuDmt_xXMfG66Rn1GA%2Fuploads%2FevsJCF6F1tx1ScZwDQOd%2FAtlassian-horizontal-blue-rgb.webp?alt=media&token=2fcd0528-e8bb-4bdd-af35-9d20e313d1a8"
70 width="150"
71 alt="Atlassian"
72 /></a>
73 </td>
74 <td valign="center">
75 <a href="https://github.com/Autodesk">
76 <img
77 src="https://876297641-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LUuDmt_xXMfG66Rn1GA%2Fuploads%2FvpTe02RdOhUJBA8TdHEE%2Fautodesk-logo-white.png?alt=media&token=326961b4-ea4f-4ded-89a4-e05692eec8ee"
78 width="150"
79 alt="Autodesk"
80 /></a>
81 </td>
82 <td valign="center">
83 <a href="https://github.com/common-voice/common-voice">
84 <img
85 src="https://876297641-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LUuDmt_xXMfG66Rn1GA%2Fuploads%2F4zPSrubNJKViAzUIftIy%2Fmozilla-logo-bw-rgb.png?alt=media&token=9f93aae2-833f-4cc4-8df9-b7fea0ad5cb5"
86 width="150"
87 alt="Mozilla"
88 /></a>
89 </td>
90 <td valign="center">
91 <a href="https://github.com/nestjs/bull">
92 <img
93 src="https://876297641-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LUuDmt_xXMfG66Rn1GA%2Fuploads%2FfAcGye182utFUtPKdLqJ%2FScreenshot%202022-02-15%20at%2011.32.39.png?alt=media&token=29feb550-f0bc-467d-a290-f700701d7d15"
94 width="150"
95 alt="Nest"
96 /></a>
97 </td>
98 <td valign="center">
99 <a href="https://github.com/salesforce/refocus">
100 <img
101 src="https://876297641-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LUuDmt_xXMfG66Rn1GA%2Fuploads%2FZNnYNuL5qJ6ZoBh7JJEW%2Fsalesforce-logo.png?alt=media&token=ddcae63b-08c0-4dd4-8496-3b29a9bf977d"
102 width="100"
103 alt="Salesforce"
104 /></a>
105 </td>
106
107 </tr>
108</table>
109
110---
111
112### 🚀 Sponsors 🚀
113
114<table cellspacing="0" cellpadding="0" border="0">
115 <tr>
116 <td>
117 <a href="https://www.dragonflydb.io/">
118 <img src="https://raw.githubusercontent.com/dragonflydb/dragonfly/main/.github/images/logo-full.svg" width=550 alt="Dragonfly" />
119 </a>
120 </td>
121 <td>
122 Dragonfly is a new Redis™ drop-in replacement that is fully compatible with BullMQ and brings some important advantages over Redis™ such as massive
123 better performance by utilizing all CPU cores available and faster and more memory efficient data structures. Read more <a href="https://www.dragonflydb.io/docs/integrations/bullmq">here</a> on how to use it with BullMQ.
124 </td>
125 </tr>
126 <tr>
127 <td>
128 <a href="https://dashboard.memetria.com/new?utm_campaign=BULLMQ">
129 <img src="https://www.memetria.com/images/logo/memetria-logo.svg" width=350 alt="Memetria for Redis" />
130 </a>
131 </td>
132 <td>
133 If you need high quality production Redis instances for your Bull project, please consider subscribing to <a href="https://dashboard.memetria.com/new?utm_campaign=BULLMQ">Memetria for Redis</a>, leaders in Redis hosting that works perfectly with BullMQ. Use the promo code "BULLMQ" when signing up to help us sponsor the development of BullMQ!
134 </td>
135 </tr>
136</table>
137
138---
139
140### Official FrontEnd
141
142[<img src="http://taskforce.sh/assets/logo_square.png" width="100" alt="Taskforce.sh, Inc" style="padding: 100px"/>](https://taskforce.sh)
143
144Supercharge your queues with a professional front end:
145- Get a complete overview of all your queues.
146- Inspect jobs, search, retry, or promote delayed jobs.
147- Metrics and statistics.
148- and many more features.
149
150Sign up at [Taskforce.sh](https://taskforce.sh)
151
152---
153
154### Bull Features
155
156- [x] Minimal CPU usage due to a polling-free design.
157- [x] Robust design based on Redis.
158- [x] Delayed jobs.
159- [x] Schedule and repeat jobs according to a cron specification.
160- [x] Rate limiter for jobs.
161- [x] Retries.
162- [x] Priority.
163- [x] Concurrency.
164- [x] Pause/resume—globally or locally.
165- [x] Multiple job types per queue.
166- [x] Threaded (sandboxed) processing functions.
167- [x] Automatic recovery from process crashes.
168
169And coming up on the roadmap...
170
171- [ ] Job completion acknowledgement (you can use the message queue [pattern](https://github.com/OptimalBits/bull/blob/develop/PATTERNS.md#returning-job-completions) in the meantime).
172- [ ] Parent-child jobs relationships.
173
174---
175
176### UIs
177
178There are a few third-party UIs that you can use for monitoring:
179
180**BullMQ**
181
182- [Taskforce](https://taskforce.sh)
183
184**Bull v3**
185
186- [Taskforce](https://taskforce.sh)
187- [bull-board](https://github.com/vcapretz/bull-board)
188- [bull-repl](https://github.com/darky/bull-repl)
189- [bull-monitor](https://github.com/s-r-x/bull-monitor)
190- [Monitoro](https://github.com/AbhilashJN/monitoro)
191
192**Bull <= v2**
193
194- [Matador](https://github.com/ShaneK/Matador)
195- [react-bull](https://github.com/kfatehi/react-bull)
196- [Toureiro](https://github.com/Epharmix/Toureiro)
197
198---
199
200### Monitoring & Alerting
201
202- With Prometheus [Bull Queue Exporter](https://github.com/UpHabit/bull_exporter)
203
204---
205
206### Feature Comparison
207
208Since there are a few job queue solutions, here is a table comparing them:
209
210| Feature | [BullMQ-Pro](https://bullmq.io/#bullmq-pro) | [BullMQ](https://bullmq.io) | Bull | Kue | Bee | Agenda |
211| :------------------------ | :-------------: | :-------------: | :-------------: | :---: | -------- | ------ |
212| Backend | redis | redis | redis | redis | redis | mongo |
213| Observables | ✓ | | | | | |
214| Group Rate Limit | ✓ | | | | | |
215| Group Support | ✓ | | | | | |
216| Batches Support | ✓ | | | | | |
217| Parent/Child Dependencies | ✓ | ✓ | | | | |
218| Priorities | ✓ | ✓ | ✓ | ✓ | | ✓ |
219| Concurrency | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
220| Delayed jobs | ✓ | ✓ | ✓ | ✓ | | ✓ |
221| Global events | ✓ | ✓ | ✓ | ✓ | | |
222| Rate Limiter | ✓ | ✓ | ✓ | | | |
223| Pause/Resume | ✓ | ✓ | ✓ | ✓ | | |
224| Sandboxed worker | ✓ | ✓ | ✓ | | | |
225| Repeatable jobs | ✓ | ✓ | ✓ | | | ✓ |
226| Atomic ops | ✓ | ✓ | ✓ | | ✓ | |
227| Persistence | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
228| UI | ✓ | ✓ | ✓ | ✓ | | ✓ |
229| Optimized for | Jobs / Messages | Jobs / Messages | Jobs / Messages | Jobs | Messages | Jobs |
230
231
232### Install
233
234```bash
235npm install bull --save
236```
237or
238
239```bash
240yarn add bull
241```
242
243_**Requirements:** Bull requires a Redis version greater than or equal to `2.8.18`._
244
245
246### Typescript Definitions
247
248```bash
249npm install @types/bull --save-dev
250```
251```bash
252yarn add --dev @types/bull
253```
254
255Definitions are currently maintained in the [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/bull) repo.
256
257
258## Contributing
259
260We welcome all types of contributions, either code fixes, new features or doc improvements.
261Code formatting is enforced by [prettier](https://prettier.io/).
262For commits please follow conventional [commits convention](https://www.conventionalcommits.org/en/v1.0.0-beta.2/).
263All code must pass lint rules and test suites before it can be merged into develop.
264
265---
266
267### Quick Guide
268
269#### Basic Usage
270```js
271const Queue = require('bull');
272
273const videoQueue = new Queue('video transcoding', 'redis://127.0.0.1:6379');
274const audioQueue = new Queue('audio transcoding', { redis: { port: 6379, host: '127.0.0.1', password: 'foobared' } }); // Specify Redis connection using object
275const imageQueue = new Queue('image transcoding');
276const pdfQueue = new Queue('pdf transcoding');
277
278videoQueue.process(function (job, done) {
279
280 // job.data contains the custom data passed when the job was created
281 // job.id contains id of this job.
282
283 // transcode video asynchronously and report progress
284 job.progress(42);
285
286 // call done when finished
287 done();
288
289 // or give an error if error
290 done(new Error('error transcoding'));
291
292 // or pass it a result
293 done(null, { framerate: 29.5 /* etc... */ });
294
295 // If the job throws an unhandled exception it is also handled correctly
296 throw new Error('some unexpected error');
297});
298
299audioQueue.process(function (job, done) {
300 // transcode audio asynchronously and report progress
301 job.progress(42);
302
303 // call done when finished
304 done();
305
306 // or give an error if error
307 done(new Error('error transcoding'));
308
309 // or pass it a result
310 done(null, { samplerate: 48000 /* etc... */ });
311
312 // If the job throws an unhandled exception it is also handled correctly
313 throw new Error('some unexpected error');
314});
315
316imageQueue.process(function (job, done) {
317 // transcode image asynchronously and report progress
318 job.progress(42);
319
320 // call done when finished
321 done();
322
323 // or give an error if error
324 done(new Error('error transcoding'));
325
326 // or pass it a result
327 done(null, { width: 1280, height: 720 /* etc... */ });
328
329 // If the job throws an unhandled exception it is also handled correctly
330 throw new Error('some unexpected error');
331});
332
333pdfQueue.process(function (job) {
334 // Processors can also return promises instead of using the done callback
335 return pdfAsyncProcessor();
336});
337
338videoQueue.add({ video: 'http://example.com/video1.mov' });
339audioQueue.add({ audio: 'http://example.com/audio1.mp3' });
340imageQueue.add({ image: 'http://example.com/image1.tiff' });
341```
342
343#### Using promises
344
345Alternatively, you can use return promises instead of using the `done` callback:
346
347```javascript
348videoQueue.process(function (job) { // don't forget to remove the done callback!
349 // Simply return a promise
350 return fetchVideo(job.data.url).then(transcodeVideo);
351
352 // Handles promise rejection
353 return Promise.reject(new Error('error transcoding'));
354
355 // Passes the value the promise is resolved with to the "completed" event
356 return Promise.resolve({ framerate: 29.5 /* etc... */ });
357
358 // If the job throws an unhandled exception it is also handled correctly
359 throw new Error('some unexpected error');
360 // same as
361 return Promise.reject(new Error('some unexpected error'));
362});
363```
364
365#### Separate processes
366
367The process function can also be run in a separate process. This has several advantages:
368- The process is sandboxed so if it crashes it does not affect the worker.
369- You can run blocking code without affecting the queue (jobs will not stall).
370- Much better utilization of multi-core CPUs.
371- Less connections to redis.
372
373In order to use this feature just create a separate file with the processor:
374```js
375// processor.js
376module.exports = function (job) {
377 // Do some heavy work
378
379 return Promise.resolve(result);
380}
381```
382
383And define the processor like this:
384
385```js
386// Single process:
387queue.process('/path/to/my/processor.js');
388
389// You can use concurrency as well:
390queue.process(5, '/path/to/my/processor.js');
391
392// and named processors:
393queue.process('my processor', 5, '/path/to/my/processor.js');
394```
395
396#### Repeated jobs
397
398A job can be added to a queue and processed repeatedly according to a cron specification:
399
400```js
401 paymentsQueue.process(function (job) {
402 // Check payments
403 });
404
405 // Repeat payment job once every day at 3:15 (am)
406 paymentsQueue.add(paymentsData, { repeat: { cron: '15 3 * * *' } });
407
408```
409
410As a tip, check your expressions here to verify they are correct:
411[cron expression generator](https://crontab.cronhub.io)
412
413#### Pause / Resume
414
415A queue can be paused and resumed globally (pass `true` to pause processing for
416just this worker):
417```js
418queue.pause().then(function () {
419 // queue is paused now
420});
421
422queue.resume().then(function () {
423 // queue is resumed now
424})
425```
426
427#### Events
428
429A queue emits some useful events, for example...
430```js
431.on('completed', function (job, result) {
432 // Job completed with output result!
433})
434```
435
436For more information on events, including the full list of events that are fired, check out the [Events reference](./REFERENCE.md#events)
437
438#### Queues performance
439
440Queues are cheap, so if you need many of them just create new ones with different
441names:
442```javascript
443const userJohn = new Queue('john');
444const userLisa = new Queue('lisa');
445.
446.
447.
448```
449
450However every queue instance will require new redis connections, check how to [reuse connections](https://github.com/OptimalBits/bull/blob/master/PATTERNS.md#reusing-redis-connections) or you can also use [named processors](https://github.com/OptimalBits/bull/blob/master/REFERENCE.md#queueprocess) to achieve a similar result.
451
452#### Cluster support
453
454NOTE: From version 3.2.0 and above it is recommended to use threaded processors instead.
455
456Queues are robust and can be run in parallel in several threads or processes
457without any risk of hazards or queue corruption. Check this simple example
458using cluster to parallelize jobs across processes:
459```js
460const Queue = require('bull');
461const cluster = require('cluster');
462
463const numWorkers = 8;
464const queue = new Queue('test concurrent queue');
465
466if (cluster.isMaster) {
467 for (let i = 0; i < numWorkers; i++) {
468 cluster.fork();
469 }
470
471 cluster.on('online', function (worker) {
472 // Let's create a few jobs for the queue workers
473 for (let i = 0; i < 500; i++) {
474 queue.add({ foo: 'bar' });
475 };
476 });
477
478 cluster.on('exit', function (worker, code, signal) {
479 console.log('worker ' + worker.process.pid + ' died');
480 });
481} else {
482 queue.process(function (job, jobDone) {
483 console.log('Job done by worker', cluster.worker.id, job.id);
484 jobDone();
485 });
486}
487```
488
489---
490
491
492### Documentation
493
494For the full documentation, check out the reference and common patterns:
495
496- [Guide](https://optimalbits.github.io/bull/) — Your starting point for developing with Bull.
497- [Reference](./REFERENCE.md) — Reference document with all objects and methods available.
498- [Patterns](./PATTERNS.md) — a set of examples for common patterns.
499- [License](./LICENSE.md) — the Bull license—it's MIT.
500
501If you see anything that could use more docs, please submit a pull request!
502
503
504
505---
506
507### Important Notes
508
509The queue aims for an "at least once" working strategy. This means that in some situations, a job
510could be processed more than once. This mostly happens when a worker fails to keep a lock
511for a given job during the total duration of the processing.
512
513When a worker is processing a job it will keep the job "locked" so other workers can't process it.
514
515It's important to understand how locking works to prevent your jobs from losing their lock - becoming _stalled_ -
516and being restarted as a result. Locking is implemented internally by creating a lock for `lockDuration` on interval
517`lockRenewTime` (which is usually half `lockDuration`). If `lockDuration` elapses before the lock can be renewed,
518the job will be considered stalled and is automatically restarted; it will be __double processed__. This can happen when:
5191. The Node process running your job processor unexpectedly terminates.
5202. Your job processor was too CPU-intensive and stalled the Node event loop, and as a result, Bull couldn't renew the job lock (see [#488](https://github.com/OptimalBits/bull/issues/488) for how we might better detect this). You can fix this by breaking your job processor into smaller parts so that no single part can block the Node event loop. Alternatively, you can pass a larger value for the `lockDuration` setting (with the tradeoff being that it will take longer to recognize a real stalled job).
521
522As such, you should always listen for the `stalled` event and log this to your error monitoring system, as this means your jobs are likely getting double-processed.
523
524As a safeguard so problematic jobs won't get restarted indefinitely (e.g. if the job processor always crashes its Node process), jobs will be recovered from a stalled state a maximum of `maxStalledCount` times (default: `1`).