[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-3286":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":24,"hasPages":24,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":35,"readmeContent":36,"aiSummary":37,"trendingCount":16,"starSnapshotCount":16,"syncStatus":38,"lastSyncTime":39,"discoverSource":40},3286,"bull","OptimalBits\u002Fbull","OptimalBits","Premium Queue package for handling distributed jobs and messages in NodeJS.","",null,"JavaScript",16244,1422,108,143,0,1,9,3,44.46,"Other",false,"develop",true,[26,27,28,29,30,31,32,33,34],"job","job-queue","message","message-queue","nodejs","priority","queue","rate-limiter","scheduler","2026-06-12 02:00:48","\n\u003Cdiv align=\"center\">\n  \u003Cbr\u002F>\n  \u003Cimg src=\".\u002Fsupport\u002Flogo@2x.png\" width=\"300\" \u002F>\n  \u003Cbr\u002F>\n  \u003Cbr\u002F>\n  \u003Cp>\n    The fastest, most reliable, Redis-based queue for Node. \u003Cbr\u002F>\n    Carefully written for rock solid stability and atomicity.\n  \u003C\u002Fp>\n  \u003Cbr\u002F>\n  \u003Cp>\n    \u003Ca href=\"#-sponsors-\">\u003Cstrong>Sponsors\u003C\u002Fstrong>\u003C\u002Fa> ·\n    \u003Ca href=\"#bull-features\">\u003Cstrong>Features\u003C\u002Fstrong>\u003C\u002Fa> ·\n    \u003Ca href=\"#uis\">\u003Cstrong>UIs\u003C\u002Fstrong>\u003C\u002Fa> ·\n    \u003Ca href=\"#install\">\u003Cstrong>Install\u003C\u002Fstrong>\u003C\u002Fa> ·\n    \u003Ca href=\"#quick-guide\">\u003Cstrong>Quick Guide\u003C\u002Fstrong>\u003C\u002Fa> ·\n    \u003Ca href=\"#documentation\">\u003Cstrong>Documentation\u003C\u002Fstrong>\u003C\u002Fa>\n  \u003C\u002Fp>\n  \u003Cp>Check the new \u003Ca href=\"https:\u002F\u002Foptimalbits.github.io\u002Fbull\u002F\">\u003Cstrong>Guide!\u003C\u002Fstrong>\u003C\u002Fp>\n  \u003Cbr\u002F>\n  \u003Cp>\n    \u003Ca href=\"https:\u002F\u002Fgitter.im\u002FOptimalBits\u002Fbull\">\n      \u003Cimg src=\"https:\u002F\u002Fbadges.gitter.im\u002FJoin%20Chat.svg\"\u002F>\n    \u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fjoin.slack.com\u002Ft\u002Fbullmq\u002Fshared_invite\u002Fzt-1nbtpk6mv-TItWpF9jf3k4yrCaS0PPZA\">\n      \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSlack-4A154B\"\u002F>\n    \u003C\u002Fa>\n    \u003Ca href=\"http:\u002F\u002Fbadge.fury.io\u002Fjs\u002Fbull\">\n      \u003Cimg src=\"https:\u002F\u002Fbadge.fury.io\u002Fjs\u002Fbull.svg\"\u002F>\n    \u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fcoveralls.io\u002Fgithub\u002FOptimalBits\u002Fbull?branch=master\">\n      \u003Cimg src=\"https:\u002F\u002Fcoveralls.io\u002Frepos\u002Fgithub\u002FOptimalBits\u002Fbull\u002Fbadge.svg?branch=master\"\u002F>\n    \u003C\u002Fa>\n    \u003Ca href=\"http:\u002F\u002Fisitmaintained.com\u002Fproject\u002FOptimalBits\u002Fbull\">\n      \u003Cimg src=\"http:\u002F\u002Fisitmaintained.com\u002Fbadge\u002Fopen\u002Foptimalbits\u002Fbull.svg\"\u002F>\n    \u003C\u002Fa>\n    \u003Ca href=\"http:\u002F\u002Fisitmaintained.com\u002Fproject\u002FOptimalBits\u002Fbull\">\n      \u003Cimg src=\"http:\u002F\u002Fisitmaintained.com\u002Fbadge\u002Fresolution\u002Foptimalbits\u002Fbull.svg\"\u002F>\n    \u003C\u002Fa>\n        \u003Ca href=\"https:\u002F\u002Ftwitter.com\u002Fmanast\">\n      \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Ftwitter\u002Ffollow\u002Fmanast?label=Stay%20updated&style=social\"\u002F>\n    \u003C\u002Fa>\n  \u003C\u002Fp>\n\u003C\u002Fdiv>\n\n### 🚀 Sponsors 🚀\n\n\u003Ctable cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n  \u003Ctr>\n    \u003Ctd>\n      \u003Ca href=\"https:\u002F\u002Fwww.dragonflydb.io\u002F\">\n        \u003Cimg src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fdragonflydb\u002Fdragonfly\u002Fmain\u002F.github\u002Fimages\u002Flogo-full.svg\" width=550 alt=\"Dragonfly\" \u002F>\n      \u003C\u002Fa>\n    \u003C\u002Ftd>\n    \u003Ctd>\n      Dragonfly is a new Redis™ drop-in replacement that is fully compatible with BullMQ and brings some important advantages over Redis™ such as massive\n      better performance by utilizing all CPU cores available and faster and more memory efficient data structures. Read more \u003Ca href=\"https:\u002F\u002Fwww.dragonflydb.io\u002Fdocs\u002Fintegrations\u002Fbullmq\">here\u003C\u002Fa> on how to use it with BullMQ.\n    \u003C\u002Ftd>\n  \u003C\u002Ftr>\n\u003C\u002Ftable>\n\n### 📻 News and updates\n\nBull is currently in maintenance mode, we are only fixing bugs. For new features check [BullMQ](https:\u002F\u002Fgithub.com\u002Ftaskforcesh\u002Fbullmq), a modern rewritten\nimplementation in Typescript. You are still very welcome to use Bull if it suits your needs, which is a safe, battle tested library.\n\nFollow me on [Twitter](http:\u002F\u002Ftwitter.com\u002Fmanast) for other important news and updates.\n\n### 🛠 Tutorials\n\nYou can find tutorials and news in this blog: https:\u002F\u002Fblog.taskforce.sh\u002F\n\n---\n\n### Used by\n\nBull is popular among large and small organizations, like the following ones:\n\n\u003Ctable cellspacing=\"0\" cellpadding=\"0\">\n  \u003Ctr>\n    \u003Ctd valign=\"center\">\n      \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fatlassian\u002Fgithub-for-jira\">\n        \u003Cimg\n          src=\"https:\u002F\u002F876297641-files.gitbook.io\u002F~\u002Ffiles\u002Fv0\u002Fb\u002Fgitbook-x-prod.appspot.com\u002Fo\u002Fspaces%2F-LUuDmt_xXMfG66Rn1GA%2Fuploads%2FevsJCF6F1tx1ScZwDQOd%2FAtlassian-horizontal-blue-rgb.webp?alt=media&token=2fcd0528-e8bb-4bdd-af35-9d20e313d1a8\"\n          width=\"150\"\n          alt=\"Atlassian\"\n      \u002F>\u003C\u002Fa>\n    \u003C\u002Ftd>\n    \u003Ctd valign=\"center\">\n      \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FAutodesk\">\n        \u003Cimg\n          src=\"https:\u002F\u002F876297641-files.gitbook.io\u002F~\u002Ffiles\u002Fv0\u002Fb\u002Fgitbook-x-prod.appspot.com\u002Fo\u002Fspaces%2F-LUuDmt_xXMfG66Rn1GA%2Fuploads%2FvpTe02RdOhUJBA8TdHEE%2Fautodesk-logo-white.png?alt=media&token=326961b4-ea4f-4ded-89a4-e05692eec8ee\"\n          width=\"150\"\n          alt=\"Autodesk\"\n      \u002F>\u003C\u002Fa>\n    \u003C\u002Ftd>\n    \u003Ctd valign=\"center\">\n      \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcommon-voice\u002Fcommon-voice\">\n        \u003Cimg\n          src=\"https:\u002F\u002F876297641-files.gitbook.io\u002F~\u002Ffiles\u002Fv0\u002Fb\u002Fgitbook-x-prod.appspot.com\u002Fo\u002Fspaces%2F-LUuDmt_xXMfG66Rn1GA%2Fuploads%2F4zPSrubNJKViAzUIftIy%2Fmozilla-logo-bw-rgb.png?alt=media&token=9f93aae2-833f-4cc4-8df9-b7fea0ad5cb5\"\n          width=\"150\"\n          alt=\"Mozilla\"\n      \u002F>\u003C\u002Fa>\n    \u003C\u002Ftd>\n    \u003Ctd valign=\"center\">\n      \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnestjs\u002Fbull\">\n        \u003Cimg\n          src=\"https:\u002F\u002F876297641-files.gitbook.io\u002F~\u002Ffiles\u002Fv0\u002Fb\u002Fgitbook-x-prod.appspot.com\u002Fo\u002Fspaces%2F-LUuDmt_xXMfG66Rn1GA%2Fuploads%2FfAcGye182utFUtPKdLqJ%2FScreenshot%202022-02-15%20at%2011.32.39.png?alt=media&token=29feb550-f0bc-467d-a290-f700701d7d15\"\n          width=\"150\"\n          alt=\"Nest\"\n      \u002F>\u003C\u002Fa>\n    \u003C\u002Ftd>\n    \u003Ctd valign=\"center\">\n      \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fsalesforce\u002Frefocus\">\n        \u003Cimg\n          src=\"https:\u002F\u002F876297641-files.gitbook.io\u002F~\u002Ffiles\u002Fv0\u002Fb\u002Fgitbook-x-prod.appspot.com\u002Fo\u002Fspaces%2F-LUuDmt_xXMfG66Rn1GA%2Fuploads%2FZNnYNuL5qJ6ZoBh7JJEW%2Fsalesforce-logo.png?alt=media&token=ddcae63b-08c0-4dd4-8496-3b29a9bf977d\"\n          width=\"100\"\n          alt=\"Salesforce\"\n      \u002F>\u003C\u002Fa>\n    \u003C\u002Ftd>\n\n  \u003C\u002Ftr>\n\u003C\u002Ftable>\n\n---\n\n---\n\n### Official FrontEnd\n\n[\u003Cimg src=\"http:\u002F\u002Ftaskforce.sh\u002Fassets\u002Flogo_square.png\" width=\"100\" alt=\"Taskforce.sh, Inc\" style=\"padding: 100px\"\u002F>](https:\u002F\u002Ftaskforce.sh)\n\nSupercharge your queues with a professional front end:\n- Get a complete overview of all your queues.\n- Inspect jobs, search, retry, or promote delayed jobs.\n- Metrics and statistics.\n- and many more features.\n\nSign up at [Taskforce.sh](https:\u002F\u002Ftaskforce.sh)\n\n---\n\n### Bull Features\n\n- [x] Minimal CPU usage due to a polling-free design.\n- [x] Robust design based on Redis.\n- [x] Delayed jobs.\n- [x] Schedule and repeat jobs according to a cron specification.\n- [x] Rate limiter for jobs.\n- [x] Retries.\n- [x] Priority.\n- [x] Concurrency.\n- [x] Pause\u002Fresume—globally or locally.\n- [x] Multiple job types per queue.\n- [x] Threaded (sandboxed) processing functions.\n- [x] Automatic recovery from process crashes.\n\nAnd coming up on the roadmap...\n\n- [ ] Job completion acknowledgement (you can use the message queue [pattern](https:\u002F\u002Fgithub.com\u002FOptimalBits\u002Fbull\u002Fblob\u002Fdevelop\u002FPATTERNS.md#returning-job-completions) in the meantime).\n- [ ] Parent-child jobs relationships.\n\n---\n\n### UIs\n\nThere are a few third-party UIs that you can use for monitoring:\n\n**BullMQ**\n\n- [Taskforce](https:\u002F\u002Ftaskforce.sh)\n\n**Bull v3**\n\n- [Taskforce](https:\u002F\u002Ftaskforce.sh)\n- [bull-board](https:\u002F\u002Fgithub.com\u002Fvcapretz\u002Fbull-board)\n- [bull-repl](https:\u002F\u002Fgithub.com\u002Fdarky\u002Fbull-repl)\n- [bull-monitor](https:\u002F\u002Fgithub.com\u002Fs-r-x\u002Fbull-monitor)\n- [Monitoro](https:\u002F\u002Fgithub.com\u002FAbhilashJN\u002Fmonitoro)\n\n**Bull \u003C= v2**\n\n- [Matador](https:\u002F\u002Fgithub.com\u002FShaneK\u002FMatador)\n- [react-bull](https:\u002F\u002Fgithub.com\u002Fkfatehi\u002Freact-bull)\n- [Toureiro](https:\u002F\u002Fgithub.com\u002FEpharmix\u002FToureiro)\n\n---\n\n### Monitoring & Alerting\n\n- With Prometheus [Bull Queue Exporter](https:\u002F\u002Fgithub.com\u002FUpHabit\u002Fbull_exporter)\n\n---\n\n### Feature Comparison\n\nSince there are a few job queue solutions, here is a table comparing them:\n\n| Feature                   |   [BullMQ-Pro](https:\u002F\u002Fbullmq.io\u002F#bullmq-pro)    |     [BullMQ](https:\u002F\u002Fbullmq.io)      |      Bull       |  Kue  | Bee      | Agenda |\n| :------------------------ | :-------------: | :-------------: | :-------------: | :---: | -------- | ------ |\n| Backend                   |      redis      |      redis      |      redis      | redis | redis    | mongo  |\n| Observables               |        ✓        |                 |                 |       |          |        |\n| Group Rate Limit          |        ✓        |                 |                 |       |          |        |\n| Group Support             |        ✓        |                 |                 |       |          |        |\n| Batches Support           |        ✓        |                 |                 |       |          |        |\n| Parent\u002FChild Dependencies |        ✓        |        ✓        |                 |       |          |        |\n| Priorities                |        ✓        |        ✓        |        ✓        |   ✓   |          | ✓      |\n| Concurrency               |        ✓        |        ✓        |        ✓        |   ✓   | ✓        | ✓      |\n| Delayed jobs              |        ✓        |        ✓        |        ✓        |   ✓   |          | ✓      |\n| Global events             |        ✓        |        ✓        |        ✓        |   ✓   |          |        |\n| Rate Limiter              |        ✓        |        ✓        |        ✓        |       |          |        |\n| Pause\u002FResume              |        ✓        |        ✓        |        ✓        |   ✓   |          |        |\n| Sandboxed worker          |        ✓        |        ✓        |        ✓        |       |          |        |\n| Repeatable jobs           |        ✓        |        ✓        |        ✓        |       |          | ✓      |\n| Atomic ops                |        ✓        |        ✓        |        ✓        |       | ✓        |        |\n| Persistence               |        ✓        |        ✓        |        ✓        |   ✓   | ✓        | ✓      |\n| UI                        |        ✓        |        ✓        |        ✓        |   ✓   |          | ✓      |\n| Optimized for             | Jobs \u002F Messages | Jobs \u002F Messages | Jobs \u002F Messages | Jobs  | Messages | Jobs   |\n\n\n### Install\n\n```bash\nnpm install bull --save\n```\nor\n\n```bash\nyarn add bull\n```\n\n_**Requirements:** Bull requires a Redis version greater than or equal to `2.8.18`._\n\n\n### Typescript Definitions\n\n```bash\nnpm install @types\u002Fbull --save-dev\n```\n```bash\nyarn add --dev @types\u002Fbull\n```\n\nDefinitions are currently maintained in the [DefinitelyTyped](https:\u002F\u002Fgithub.com\u002FDefinitelyTyped\u002FDefinitelyTyped\u002Ftree\u002Fmaster\u002Ftypes\u002Fbull) repo.\n\n\n## Contributing\n\nWe welcome all types of contributions, either code fixes, new features or doc improvements.\nCode formatting is enforced by [prettier](https:\u002F\u002Fprettier.io\u002F).\nFor commits please follow conventional [commits convention](https:\u002F\u002Fwww.conventionalcommits.org\u002Fen\u002Fv1.0.0-beta.2\u002F).\nAll code must pass lint rules and test suites before it can be merged into develop.\n\n---\n\n### Quick Guide\n\n#### Basic Usage\n```js\nconst Queue = require('bull');\n\nconst videoQueue = new Queue('video transcoding', 'redis:\u002F\u002F127.0.0.1:6379');\nconst audioQueue = new Queue('audio transcoding', { redis: { port: 6379, host: '127.0.0.1', password: 'foobared' } }); \u002F\u002F Specify Redis connection using object\nconst imageQueue = new Queue('image transcoding');\nconst pdfQueue = new Queue('pdf transcoding');\n\nvideoQueue.process(function (job, done) {\n\n  \u002F\u002F job.data contains the custom data passed when the job was created\n  \u002F\u002F job.id contains id of this job.\n\n  \u002F\u002F transcode video asynchronously and report progress\n  job.progress(42);\n\n  \u002F\u002F call done when finished\n  done();\n\n  \u002F\u002F or give an error if error\n  done(new Error('error transcoding'));\n\n  \u002F\u002F or pass it a result\n  done(null, { framerate: 29.5 \u002F* etc... *\u002F });\n\n  \u002F\u002F If the job throws an unhandled exception it is also handled correctly\n  throw new Error('some unexpected error');\n});\n\naudioQueue.process(function (job, done) {\n  \u002F\u002F transcode audio asynchronously and report progress\n  job.progress(42);\n\n  \u002F\u002F call done when finished\n  done();\n\n  \u002F\u002F or give an error if error\n  done(new Error('error transcoding'));\n\n  \u002F\u002F or pass it a result\n  done(null, { samplerate: 48000 \u002F* etc... *\u002F });\n\n  \u002F\u002F If the job throws an unhandled exception it is also handled correctly\n  throw new Error('some unexpected error');\n});\n\nimageQueue.process(function (job, done) {\n  \u002F\u002F transcode image asynchronously and report progress\n  job.progress(42);\n\n  \u002F\u002F call done when finished\n  done();\n\n  \u002F\u002F or give an error if error\n  done(new Error('error transcoding'));\n\n  \u002F\u002F or pass it a result\n  done(null, { width: 1280, height: 720 \u002F* etc... *\u002F });\n\n  \u002F\u002F If the job throws an unhandled exception it is also handled correctly\n  throw new Error('some unexpected error');\n});\n\npdfQueue.process(function (job) {\n  \u002F\u002F Processors can also return promises instead of using the done callback\n  return pdfAsyncProcessor();\n});\n\nvideoQueue.add({ video: 'http:\u002F\u002Fexample.com\u002Fvideo1.mov' });\naudioQueue.add({ audio: 'http:\u002F\u002Fexample.com\u002Faudio1.mp3' });\nimageQueue.add({ image: 'http:\u002F\u002Fexample.com\u002Fimage1.tiff' });\n```\n\n#### Using promises\n\nAlternatively, you can return promises instead of using the `done` callback:\n\n```javascript\nvideoQueue.process(function (job) { \u002F\u002F don't forget to remove the done callback!\n  \u002F\u002F Simply return a promise\n  return fetchVideo(job.data.url).then(transcodeVideo);\n\n  \u002F\u002F Handles promise rejection\n  return Promise.reject(new Error('error transcoding'));\n\n  \u002F\u002F Passes the value the promise is resolved with to the \"completed\" event\n  return Promise.resolve({ framerate: 29.5 \u002F* etc... *\u002F });\n\n  \u002F\u002F If the job throws an unhandled exception it is also handled correctly\n  throw new Error('some unexpected error');\n  \u002F\u002F same as\n  return Promise.reject(new Error('some unexpected error'));\n});\n```\n\n#### Separate processes\n\nThe process function can also be run in a separate process. This has several advantages:\n- The process is sandboxed so if it crashes it does not affect the worker.\n- You can run blocking code without affecting the queue (jobs will not stall).\n- Much better utilization of multi-core CPUs.\n- Less connections to redis.\n\nIn order to use this feature just create a separate file with the processor:\n```js\n\u002F\u002F processor.js\nmodule.exports = function (job) {\n  \u002F\u002F Do some heavy work\n\n  return Promise.resolve(result);\n}\n```\n\nAnd define the processor like this:\n\n```js\n\u002F\u002F Single process:\nqueue.process('\u002Fpath\u002Fto\u002Fmy\u002Fprocessor.js');\n\n\u002F\u002F You can use concurrency as well:\nqueue.process(5, '\u002Fpath\u002Fto\u002Fmy\u002Fprocessor.js');\n\n\u002F\u002F and named processors:\nqueue.process('my processor', 5, '\u002Fpath\u002Fto\u002Fmy\u002Fprocessor.js');\n```\n\n#### Repeated jobs\n\nA job can be added to a queue and processed repeatedly according to a cron specification:\n\n```js\n  paymentsQueue.process(function (job) {\n    \u002F\u002F Check payments\n  });\n\n  \u002F\u002F Repeat payment job once every day at 3:15 (am)\n  paymentsQueue.add(paymentsData, { repeat: { cron: '15 3 * * *' } });\n\n```\n\nAs a tip, check your expressions here to verify they are correct:\n[cron expression generator](https:\u002F\u002Fcrontab.cronhub.io)\n\n#### Pause \u002F Resume\n\nA queue can be paused and resumed globally (pass `true` to pause processing for\njust this worker):\n```js\nqueue.pause().then(function () {\n  \u002F\u002F queue is paused now\n});\n\nqueue.resume().then(function () {\n  \u002F\u002F queue is resumed now\n})\n```\n\n#### Events\n\nA queue emits some useful events, for example...\n```js\n.on('completed', function (job, result) {\n  \u002F\u002F Job completed with output result!\n})\n```\n\nFor more information on events, including the full list of events that are fired, check out the [Events reference](.\u002FREFERENCE.md#events)\n\n#### Queues performance\n\nQueues are cheap, so if you need many of them just create new ones with different\nnames:\n```javascript\nconst userJohn = new Queue('john');\nconst userLisa = new Queue('lisa');\n.\n.\n.\n```\n\nHowever every queue instance will require new redis connections, check how to [reuse connections](https:\u002F\u002Fgithub.com\u002FOptimalBits\u002Fbull\u002Fblob\u002Fmaster\u002FPATTERNS.md#reusing-redis-connections) or you can also use [named processors](https:\u002F\u002Fgithub.com\u002FOptimalBits\u002Fbull\u002Fblob\u002Fmaster\u002FREFERENCE.md#queueprocess) to achieve a similar result.\n\n#### Cluster support\n\nNOTE: From version 3.2.0 and above it is recommended to use threaded processors instead.\n\nQueues are robust and can be run in parallel in several threads or processes\nwithout any risk of hazards or queue corruption. Check this simple example\nusing cluster to parallelize jobs across processes:\n```js\nconst Queue = require('bull');\nconst cluster = require('cluster');\n\nconst numWorkers = 8;\nconst queue = new Queue('test concurrent queue');\n\nif (cluster.isMaster) {\n  for (let i = 0; i \u003C numWorkers; i++) {\n    cluster.fork();\n  }\n\n  cluster.on('online', function (worker) {\n    \u002F\u002F Let's create a few jobs for the queue workers\n    for (let i = 0; i \u003C 500; i++) {\n      queue.add({ foo: 'bar' });\n    };\n  });\n\n  cluster.on('exit', function (worker, code, signal) {\n    console.log('worker ' + worker.process.pid + ' died');\n  });\n} else {\n  queue.process(function (job, jobDone) {\n    console.log('Job done by worker', cluster.worker.id, job.id);\n    jobDone();\n  });\n}\n```\n\n---\n\n\n### Documentation\n\nFor the full documentation, check out the reference and common patterns:\n\n- [Guide](https:\u002F\u002Foptimalbits.github.io\u002Fbull\u002F) — Your starting point for developing with Bull.\n- [Reference](.\u002FREFERENCE.md) — Reference document with all objects and methods available.\n- [Patterns](.\u002FPATTERNS.md) — a set of examples for common patterns.\n- [License](.\u002FLICENSE.md) — the Bull license—it's MIT.\n\nIf you see anything that could use more docs, please submit a pull request!\n\n\n\n---\n\n### Important Notes\n\nThe queue aims for an \"at least once\" working strategy. This means that in some situations, a job\ncould be processed more than once. This mostly happens when a worker fails to keep a lock\nfor a given job during the total duration of the processing.\n\nWhen a worker is processing a job it will keep the job \"locked\" so other workers can't process it.\n\nIt's important to understand how locking works to prevent your jobs from losing their lock - becoming _stalled_ -\nand being restarted as a result. Locking is implemented internally by creating a lock for `lockDuration` on interval\n`lockRenewTime` (which is usually half `lockDuration`). If `lockDuration` elapses before the lock can be renewed,\nthe job will be considered stalled and is automatically restarted; it will be __double processed__. This can happen when:\n1. The Node process running your job processor unexpectedly terminates.\n2. 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:\u002F\u002Fgithub.com\u002FOptimalBits\u002Fbull\u002Fissues\u002F488) 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).\n\nAs 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.\n\nAs 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`).\n","Bull 是一个用于处理 Node.js 分布式任务和消息的高级队列包。它基于 Redis，提供高性能、高可靠性和原子性操作，确保任务处理过程中的稳定性和数据一致性。支持优先级调度、速率限制等功能，适用于需要异步处理大量任务或消息的应用场景，如批处理作业、定时任务以及分布式系统中的消息传递等。其简洁的 API 和强大的功能使其成为构建复杂后端服务的理想选择。",2,"2026-06-11 02:53:20","top_language"]