bull
semver
>=3.0.0 <5.0.0postconditions47functions26last verified2026-06-11coverage score84%Postconditions — what we check
- Queue.process · missing-error-handlererrorWhenJob processor doesn't handle errors via try-catch or done(error)Throws
Throws unhandled exception that Bull captures and marks job as failedRequired handlingCaller MUST handle errors in job processor using either: 1. try-catch in async processors: try { await work(); } catch (err) { throw err; } 2. done(error) in callback processors: done(err) Without error handling, exceptions are caught by Bull but may not be logged, and failed jobs accumulate silently if no 'failed' event listener exists.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - Queue.process · missing-failed-listenererrorWhenQueue instance created without 'failed' event listenerThrows
Emits 'failed' event when job failsRequired handlingCaller MUST attach 'failed' event listener to queue instance. Without this listener, failed jobs are silently lost with no visibility. CRITICAL: This is production bug #2 (60-70% of codebases). Always add: queue.on('failed', (job, err) => { logger.error('Job failed:', err); })costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - Queue.process · missing-stalled-listenererrorWhenQueue instance created without 'stalled' event listenerThrows
Emits 'stalled' event when job stalls (CPU-intensive code blocks event loop)Required handlingCaller MUST attach 'stalled' event listener to queue instance. Stalled jobs are restarted by another worker, resulting in DUPLICATE PROCESSING. CRITICAL: This is production bug #1 (80-90% of codebases don't detect this). Impact: Duplicate emails, duplicate payments, data corruption. Always add: queue.on('stalled', (job) => { logger.error('Job stalled:', job.id); })costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[3] - Queue.process · missing-error-listenerwarningWhenQueue instance created without 'error' event listenerThrows
Emits 'error' event for Redis connection errors, queue errorsRequired handlingCaller SHOULD attach 'error' event listener to queue instance. Without this listener, Redis connection errors and queue errors go unnoticed. Always add: queue.on('error', (error) => { logger.error('Queue error:', error); })costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - new Queue · missing-queue-listenerserrorWhenQueue instantiated without required event listenersThrows
Queue emits events but no listeners attachedRequired handlingAfter creating Queue instance, MUST attach event listeners: 1. queue.on('failed', ...) - REQUIRED 2. queue.on('stalled', ...) - CRITICAL 3. queue.on('error', ...) - RECOMMENDED See Queue.process postconditions for details.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - Queue.add · add-redis-connection-errorerrorWhenQueue.add() called when Redis connection is unavailableRequired handlingCaller MUST wrap Queue.add() in try-catch and handle the Error. Without error handling, jobs are lost silently with no retry mechanism. Always log the error and implement a fallback (retry queue, dead letter, alert).costhighin prodimmediate exceptionusers seedegraded performancevisibilitysilent
- Queue.add · add-duplicate-job-id-silent-dropwarningWhenQueue.add() called with a jobId that already exists in the queueRequired handlingCaller SHOULD verify job creation by checking the returned Job object or querying job status after add(). For deduplication flows, document the intent explicitly. Do not assume add() always creates a new job.costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[1]
- Queue.addBulk · addbulk-redis-connection-errorerrorWhenQueue.addBulk() called when Redis connection is unavailableRequired handlingCaller MUST wrap Queue.addBulk() in try-catch and handle the Error. On failure, the entire batch is lost — implement retry logic or store the batch for resubmission. Log the error with the batch contents.costhighin prodimmediate exceptionusers seedegraded performancevisibilitysilentSources[1]
- Queue.addBulk · addbulk-repeat-option-unsupportedwarningWhenQueue.addBulk() called with jobs that include a repeat optionRequired handlingDo not use the repeat option with addBulk(). Add repeating jobs individually via Queue.add() to ensure the repeat scheduler is engaged.costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[5]
- Queue.close · close-not-called-on-shutdownerrorWhenQueue instance created without registering a SIGTERM/shutdown handler that calls Queue.close()Required handlingCaller MUST register process shutdown handlers that call queue.close(). Add: process.on('SIGTERM', () => queue.close()) and: process.on('SIGINT', () => queue.close()) Without this, the process hangs on exit and leaks Redis connections.costmediumin proddelayed failureusers seeservice unavailablevisibilityvisibleSources[1]
- Queue.close · close-called-mid-jobwarningWhenQueue.close() called while active jobs are still processingRequired handlingSet a timeout for graceful shutdown. If shutdown takes too long, force exit with process.exit(). Document the expected shutdown behavior.costmediumin proddelayed failureusers seeservice unavailablevisibilityvisibleSources[1]
- Queue.obliterate · obliterate-active-jobs-errorerrorWhenobliterate() called without opts.force=true while active jobs are processingThrows
Error('Cannot obliterate queue that has active jobs, use force option to force obliteration')Required handlingCaller MUST wrap obliterate() in try-catch. Drain the queue first (pause, wait for active jobs), or use obliterate({ force: true }) only when data loss is acceptable (test environments). Never call obliterate() in production request handlers without explicit environment guards.costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[6] - Queue.obliterate · obliterate-irreversibleerrorWhenobliterate() completes successfully in a production environment without environment guardsThrows
No error — but all queue data is permanently deleted from Redis with no undoRequired handlingCallers MUST add explicit environment guards: check NODE_ENV !== 'production' before calling obliterate(). Add admin confirmation for any production use. There is no undo — once obliterated, all job history is gone.costhighin prodsilent failureusers seeservice unavailablevisibilitysilentSources[7] - Queue.clean · clean-missing-grace-perioderrorWhenclean() called with grace argument as undefined or nullThrows
Error('You must define a grace period.')Required handlingAlways provide the grace period as first argument (milliseconds). Example: queue.clean(7 * 24 * 3600 * 1000, 'completed'). Wrap in try-catch in maintenance scripts and cron jobs.costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[6] - Queue.clean · clean-invalid-typeerrorWhenclean() type argument is not one of: 'completed', 'wait', 'active', 'paused', 'delayed', 'failed'Throws
Error('Cannot clean unknown queue type <type>')Required handlingUse 'wait' (not 'waiting') for waiting jobs. Valid type strings are: 'completed', 'wait', 'active', 'paused', 'delayed', 'failed'. The monitoring API returns 'waiting' but clean() requires 'wait'. Wrap in try-catch to handle invalid type errors gracefully.costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[6] - Job.retry · retry-job-not-existerrorWhenJob.retry() called on a job that no longer exists in RedisRequired handlingCaller MUST wrap Job.retry() in try-catch. Verify job existence before retrying, or handle the not-exist error gracefully in admin retry flows.costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[1]
- Job.retry · retry-job-not-failederrorWhenJob.retry() called on a job that is not in the failed stateRequired handlingCaller MUST wrap Job.retry() in try-catch and handle the not-failed error. Check job.getState() before retrying, or catch and ignore this specific error in bulk retry flows.costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[1]
- Queue.empty · empty-concurrent-add-racewarningWhenQueue.empty() called while another process is concurrently adding jobs to the queueRequired handlingCaller SHOULD pause the queue before emptying to prevent concurrent adds. Add: await queue.pause(); await queue.empty(); await queue.resume(). In single-process environments without concurrent writers, this is safe. In clustered environments, coordinate empty() with a distributed lock.costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
- Queue.empty · empty-zombie-jobs-on-crasherrorWhenQueue.empty() called without wrapping in try-catch, and Redis connection drops mid-executionRequired handlingCaller MUST wrap Queue.empty() in try-catch. On error, scan Redis for orphaned job keys and clean them up. Consider using Queue.obliterate({ force: true }) for a more complete cleanup in test environments, as obliterate uses Lua scripts that are more atomic than empty().costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[6]
- Queue.removeRepeatable · remove-repeatable-silent-mismatcherrorWhenremoveRepeatable() called with RepeatOpts that do not exactly match the scheduled job's optionsRequired handlingAfter calling removeRepeatable(), verify removal by calling queue.getRepeatableJobs() and checking that the job no longer appears. Store the exact RepeatOpts used at creation time (e.g. in a database) to ensure they can be reproduced exactly for removal.costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
- Queue.removeRepeatableByKey · remove-repeatable-by-key-silent-noopwarningWhenremoveRepeatableByKey() called with a key that no longer exists or was already removedRequired handlingFor idempotent cleanup, the silent no-op behavior is acceptable by design. For critical schedule removal, verify with queue.getRepeatableJobs() after calling. Log the key being removed so that failed removals can be diagnosed via monitoring.costlowin prodsilent failureusers seedegraded performancevisibilitysilentSources[9]
- Job.progress · progress-job-key-missingerrorWhenjob.progress(value) called after the job has been deleted from Redis (e.g. removeOnComplete/removeOnFail removed it, or Redis eviction)Required handlingCallers SHOULD wrap job.progress() in try-catch within the processor function. Alternatively, avoid calling progress() after performing operations that might cause the job to be considered complete (e.g. don't call progress after the business operation that marks success — the job may have been cleaned up).costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[11]
- Job.progress · progress-lock-losterrorWhenjob.progress(value) called after the job's lock has expired or been taken by another workerRequired handlingCaller SHOULD check for this error in progress() and immediately abort the processor function to prevent further duplicate work: try { await job.progress(50); } catch (err) { if (err.message.includes('Missing lock')) return; throw err; } Increase lockDuration and lockRenewTime to prevent premature lock expiry.costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[11]
- Job.update · update-job-not-activeerrorWhenjob.update(data) called on a job that is no longer in the active stateRequired handlingCaller MUST wrap job.update() in try-catch. Store critical intermediate state in external storage (database) rather than relying solely on job.update() — Redis TTL and job removal policies can destroy this data unexpectedly.costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisible
- Job.remove · remove-active-job-errorerrorWhenjob.remove() called on a job that is currently being processed (active/locked)Throws
Error('Could not remove job <id>')Required handlingCaller MUST wrap job.remove() in try-catch. Check job state before removal: const state = await job.getState(); if (state !== 'active') { await job.remove(); } For active jobs, consider using queue.pause() to stop new jobs, then wait for the active job to finish before removing it.costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[12] - Job.promote · promote-not-delayed-errorerrorWhenjob.promote() called on a job that is not in the delayed stateThrows
Error('Job <id> is not in a delayed state')Required handlingCaller MUST wrap job.promote() in try-catch. Check job.getState() immediately before calling promote() and handle the 'not-delayed' error gracefully: const state = await job.getState(); if (state === 'delayed') { await job.promote(); }costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[12] - Job.log · log-job-key-missingwarningWhenjob.log(row) called after the job has been deleted from RedisRequired handlingCaller SHOULD await job.log() and wrap in try-catch, or attach .catch() to the returned promise. Pattern: await job.log('Step complete').catch(err => logger.warn('Log failed:', err.message)); This prevents unhandled rejections without blocking the processor.costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[11]
- Job.finished · finished-job-failederrorWhenAwaiting Job.finished() when the job transitions to failed stateRequired handlingCaller MUST wrap await job.finished() in try-catch. On rejection, inspect the error message (which equals job.failedReason) and handle accordingly.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1]
- Job.finished · finished-queue-closingerrorWhenQueue.close() is called while job.finished() promise is pendingRequired handlingCaller MUST wrap await job.finished() in try-catch. During shutdown, handle the queue-closing rejection gracefully rather than crashing. Consider a timeout pattern: Promise.race([job.finished(), shutdownTimeout]).costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1]
- Queue.whenCurrentJobsFinished · when-current-jobs-not-awaited-before-closeerrorWhenQueue.close() called without first awaiting Queue.whenCurrentJobsFinished()Required handlingCaller MUST await queue.whenCurrentJobsFinished() before calling queue.close() in shutdown handlers: process.on('SIGTERM', async () => { await queue.whenCurrentJobsFinished(); await queue.close(); process.exit(0); });costhighin prodsilent failureusers seelost datavisibilitysilentSources[6]
- Queue.whenCurrentJobsFinished · when-current-jobs-no-sigterm-handlerwarningWhenwhenCurrentJobsFinished() not called in SIGTERM/SIGINT handlerRequired handlingRegister shutdown handlers that call whenCurrentJobsFinished() before exit: process.once('SIGTERM', async () => { await queue.whenCurrentJobsFinished(); await queue.close(); process.exit(0); });costmediumin prodsilent failureusers seelost datavisibilitysilentSources[6]
- Queue.removeJobs · remove-jobs-no-try-catcherrorWhenawait queue.removeJobs(pattern) called without try-catchThrows
ioredis Error (Redis connection loss or queue.isReady() rejection)Required handlingCaller MUST wrap await queue.removeJobs(pattern) in try-catch. On Redis connection error, log and retry after reconnection. Zero matches is not an error.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[12] - Queue.removeJobs · remove-jobs-pattern-blocks-rediswarningWhenQueue.removeJobs(pattern) called with broad pattern on large Redis keyspaceRequired handlingAvoid removeJobs() with broad patterns in production during peak load. Prefer Queue.clean() with a specific status and grace period for bulk cleanup. If removeJobs() is required, use narrow patterns or schedule during off-peak hours.costmediumin proddegraded serviceusers seedegraded performancevisibilityvisibleSources[12]
- Job.releaseLock · release-lock-not-ownererrorWhenjob.releaseLock() called when current worker does not own the job lockThrows
Error('Could not release lock for job <jobId>')Required handlingCaller MUST wrap await job.releaseLock() in try-catch. Only call releaseLock() from within the job's own processor function context or after acquiring the lock via job.takeLock(). Do not call releaseLock() from outside the processing context.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[12] - Job.releaseLock · release-lock-no-try-catcherrorWhenawait job.releaseLock() called without try-catchThrows
ioredis Error (Redis connection loss during Lua script execution)Required handlingCaller MUST wrap await job.releaseLock() in try-catch. Log and handle appropriately. On Redis connection error, do not retry — the lock expires via TTL automatically.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[12] - Job.moveToFailed · move-to-failed-no-try-catcherrorWhenawait job.moveToFailed(errorInfo) called without try-catchThrows
Error (Redis Lua script error codes: -1 missing key, -3 wrong state, -6 lock mismatch)Required handlingCaller MUST wrap await job.moveToFailed({ message: err.message }) in try-catch. On -1 (missing key): job was already cleaned up — log and skip. On -3 (wrong state): job completed via another path — log and skip. On -6 (lock mismatch): another worker took ownership — do not retry.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - Job.moveToFailed · move-to-failed-retry-not-exhaustedwarningWhenjob.moveToFailed() called when job still has remaining retry attemptsRequired handlingIf the intent is to force the job to the failed state immediately (bypassing retries), call job.discard() first, then job.moveToFailed(). Without discard(), the job will retry per its configured attempts and backoff settings.costlowin prodsilent failureusers seelost datavisibilitysilentSources[12]
- Job.extendLock · extend-lock-zero-return-uncheckederrorWhenawait job.extendLock(duration) is called without checking the return value, and the function returns 0 (lock not extended because lock expired or was taken by another worker).Throws
Does not throw — returns 0 silently when lock extension failsRequired handlingCaller MUST check the return value of extendLock(): const extended = await job.extendLock(30000); if (\!extended) { // Another worker took the job — stop processing immediately throw new Error('Lock lost: job taken by another worker'); } Without this check, duplicate processing causes duplicate emails, duplicate payments, or data corruption in distributed queue deployments.costhighin prodsilent failureusers seelost datavisibilitysilent - Job.extendLock · extend-lock-no-try-catchwarningWhenawait job.extendLock(duration) called without try-catch and Redis connection is unavailable or the Redis command fails.Throws
Error (ioredis connection error or NOSCRIPT error if Lua script not loaded)Required handlingWrap extendLock() calls in try-catch. On Redis error, abort the current job processing and let Bull's stall detection handle requeuing: try { const extended = await job.extendLock(this.settings.lockDuration); if (\!extended) throw new Error('Lock lost'); } catch (err) { logger.error('Lock renewal failed, aborting job', err); return; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[6] - Queue.getMetrics · get-metrics-metrics-not-enabledwarningWhenqueue.getMetrics() called but metrics were not enabled in QueueOptions (no `metrics` key in queue constructor options).Throws
Does not throw — returns zeroed data structure silentlyRequired handlingEnsure metrics are enabled in the queue constructor: const queue = new Queue('jobs', { metrics: { maxDataPoints: 1440 } }); Document to callers that zero results may mean metrics are disabled, not that no jobs have run.costlowin prodsilent failureusers seedegraded performancevisibilitysilent - Queue.getMetrics · get-metrics-no-try-catchwarningWhenawait queue.getMetrics('completed') called without try-catch and Redis pipeline exec returns an error in the first or second result.Throws
Error (Redis pipeline error from multi.exec)Required handlingWrap getMetrics() in try-catch in dashboard/monitoring code: try { const metrics = await queue.getMetrics('completed'); return metrics.data; } catch (err) { logger.error('Failed to fetch queue metrics:', err); return []; }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[14] - Job.moveToCompleted · move-to-completed-return-value-not-serializableerrorWhenjob.moveToCompleted(returnValue) called with a non-JSON-serializable returnValueThrows
TypeError: Converting circular structure to JSON / TypeError: Do not know how to serialize a BigIntRequired handlingCaller MUST ensure the returnValue is JSON-serializable before calling moveToCompleted(). Use try-catch and validate the return value: // WRONG — throws if result contains circular references or BigInt await job.moveToCompleted(processResult); // CORRECT — validate before completing try { JSON.stringify(processResult); // test serialization } catch (err) { // Return a safe summary instead of the full object await job.moveToCompleted({ status: 'completed', error: 'result-not-serializable' }); return; } await job.moveToCompleted(processResult); Common causes: circular references in database ORM objects (Prisma result objects may have circular prototype chains), BigInt fields from PostgreSQL, or custom class instances with non-serializable properties.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[12] - Job.moveToCompleted · move-to-completed-no-try-catcherrorWhenawait job.moveToCompleted() called without try-catch, and the job is no longer in the active state, the lock has expired, or the lock is owned by another worker.Throws
Error('Missing key for job <id> finished') — job deleted from Redis (removeOnComplete, eviction); Error('Missing lock for job <id> finished') — lock expired before completion; Error('Job <id> is not in the active state. finished') — job completed/failed via another path; Error('Lock mismatch for job <id>...') — another worker took the lock.Required handlingCaller MUST wrap await job.moveToCompleted() in try-catch. Handle each error type: - Missing key: log and skip (job was cleaned up by another mechanism) - Missing lock / Lock mismatch: another worker owns the job — abort immediately to prevent duplicate completion; do not re-attempt moveToCompleted. - Wrong state: job already completed or failed — check getState() before retrying. try { await job.moveToCompleted(returnValue); } catch (err) { if (err.message.includes('Missing lock') || err.message.includes('Lock mismatch')) { logger.warn('Lock lost before completion, aborting', { jobId: job.id }); return; // Stop processing — another worker will handle this job } if (err.message.includes('Missing key')) { logger.warn('Job cleaned up before completion', { jobId: job.id }); return; // Job was removed (removeOnComplete with short TTL) } throw err; // Unexpected error — re-throw }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - Job.takeLock · take-lock-false-return-uncheckederrorWhenawait job.takeLock() is called without checking the return value, and returns false (lock held by another worker or lock already acquired by this worker).Throws
Does not throw — returns false silently when lock cannot be acquiredRequired handlingCaller MUST check the return value. false means the lock could not be acquired: // WRONG — proceeds even if lock was not acquired await job.takeLock(); await doExclusiveWork(); // NOT safe — another worker may also be doing this! // CORRECT — check return value before proceeding const lock = await job.takeLock(); if (!lock) { logger.warn('Could not acquire lock for job, another worker owns it', { jobId: job.id }); return; // Abort — do not proceed with exclusive work } await doExclusiveWork(); await job.releaseLock(); // Always release in finally block Failing to check the return value leads to duplicate processing when multiple workers attempt to take the same lock — the same critical race condition as extendLock returning 0 without being checked.costhighin prodsilent failureusers seelost datavisibilitysilentSources[12] - Job.takeLock · take-lock-no-try-catchwarningWhenawait job.takeLock() called without try-catch and Redis connection is unavailableThrows
Error (ioredis connection error or NOSCRIPT error if Lua script not cached)Required handlingWrap await job.takeLock() in try-catch. On Redis error, do not proceed with exclusive work — treat it as a lock acquisition failure and abort: let lock; try { lock = await job.takeLock(); } catch (err) { logger.error('Redis error acquiring job lock', { jobId: job.id, err }); return; // Cannot determine lock state — abort } if (!lock) { return; } // Lock held by another workercostlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[12] - Queue.pause · pause-no-try-catcherrorWhenawait queue.pause() called without try-catch and Redis connection is unavailableThrows
Error (ioredis connection error from isReady() or scripts.pause() Lua execution)Required handlingWrap await queue.pause() in try-catch in shutdown handlers and maintenance scripts. A Redis error during global pause means the pause state was NOT persisted — other workers will continue processing jobs. Retry or alert on failure: try { await queue.pause(); logger.info('Queue paused globally'); } catch (err) { logger.error('Failed to pause queue — workers may still be processing', err); // Alert ops team — jobs are still processing despite shutdown attempt }costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[6] - Queue.pause · pause-local-hangs-on-stuck-jobwarningWhenqueue.pause(true) called with isLocal=true (without doNotWaitActive=true), and there is a job currently in the active state that never completes (stuck/hung processor).Throws
Does not throw — hangs indefinitely awaiting whenCurrentJobsFinished()Required handlingWhen using local pause in shutdown handlers, always set a timeout and use doNotWaitActive=true for immediate termination, or race with a timeout: // RISKY — hangs forever if a job is stuck await queue.pause(true); // SAFE — use doNotWaitActive for immediate local pause await queue.pause(true, true); // Returns immediately without waiting // SAFE — with timeout for graceful-but-bounded shutdown await Promise.race([ queue.pause(true), new Promise((_, reject) => setTimeout(() => reject(new Error('Pause timeout')), 30000)) ]).catch(() => queue.pause(true, true)); // Force pause if timeoutcostmediumin proddelayed failureusers seeservice unavailablevisibilityvisibleSources[6]
Sources
Every postcondition cites at least one of these. Numbered to match the footnotes above.
- [1]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull
- [2]blog.logrocket.com/asynchronous-task-processing-in-node-js-with-bullhttps://blog.logrocket.com/asynchronous-task-processing-in-node-js-with-bull/
- [3]docs.bullmq.io/guide/jobshttps://docs.bullmq.io/guide/jobs/stalled
- [4]github.com/luin/ioredishttps://github.com/luin/ioredis
- [5]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/issues/1731
- [6]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/queue.js
- [7]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/REFERENCE.md
- [8]raw.githubusercontent.com/OptimalBits/bullhttps://raw.githubusercontent.com/OptimalBits/bull/master/REFERENCE.md
- [9]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/repeatable.js
- [10]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/scripts/removeRepeatable-2.js
- [11]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/scripts.js
- [12]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/job.js
- [13]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/commands/extendLock-2.lua
- [14]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/getters.js
- [15]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/README.md
Need a different package?
Request a profile