Skip to content
Get started
Get started
Queue Control and Maintenance
View Markdown
guide · queue

Pause it, drain it, wipe it.

The operational verbs. Stop consumption during an incident, clear a backlog, remove finished jobs before they accumulate, and push delayed work forward when you cannot wait.

queue.pause(); // Workers stop pulling (fire-and-forget)
queue.resume(); // Back to normal (fire-and-forget)
queue.drain(); // Remove all waiting/delayed jobs (fire-and-forget)
queue.obliterate(); // Remove ALL queue data (fire-and-forget)
await queue.pauseAsync(); // Pause and wait for it
await queue.resumeAsync(); // Resume and wait for it
const n = await queue.drainAsync(); // Drain, wait, get removed count
await queue.obliterateAsync(); // Remove ALL queue data and wait for it
queue.remove('job-id'); // Remove one job (fire-and-forget)
await queue.removeAsync('job-id'); // Remove one job (pending or completed) and wait for it
await queue.waitUntilReady(); // Wait until queue/server is ready
queue.close(); // Close TCP connection (embedded: write buffered adds to SQLite)

Gotcha: in TCP mode the fire-and-forget forms return before the server has processed them. If you drain or obliterate and immediately add new jobs, the wipe can land after the add and delete the new job. Use the Async variants when the next step depends on the command being done. A fire-and-forget command that fails (for example, the broker is unreachable) is logged as one line naming the command and the queue and never crashes the process; the Async variants reject to the caller instead.

removeAsync(id) and job.remove() remove a job that is waiting, delayed, prioritized or waiting for children, and also a completed job: its data and its result are deleted, so “read the result, then make sure the job is gone” works. A payload-free completion record stays behind (the same one removeOnComplete keeps), so jobs that depend on the removed one still run.

const result = await job.waitUntilFinished(null, 30_000); // read the result first
await job.remove(); // then delete data and result
await queue.getJobState(job.id); // 'unknown'
await job.remove(); // a second call still resolves

An active job (including the job a processor is running, when it calls job.remove() on itself) and a failed job are never removed: the awaited call rejects with the job’s state, for example Job cannot be cancelled: not in queue (current state: active). Removing a job that does not exist resolves, so cleanup code can call it twice. Failed jobs leave through the DLQ. The fire-and-forget queue.remove(id) cannot report a refusal; use removeAsync when it matters. On PostgreSQL, a waiting job that another live job still depends on is refused too, with Job cannot be cancelled: not in queue (current state: waiting); remove the dependent job first.

Connected to a broker of version 2.9.13 or earlier, which answers every refusal (an active, failed or completed job too) with Job not found or cannot be cancelled, the client reads the job’s state before deciding: the call resolves only when the job no longer exists, and otherwise rejects with Job <id> cannot be removed (current state: active) (or the job’s other state). A completed job is never removed by such a broker.

Removing a job deletes the job, its result and what the broker holds for it in memory. It does not rewrite history: events already recorded in the queue’s bounded event journal (queue_events on SQLite, bunqueue_events on PostgreSQL), such as the completed event that carried the return value, stay there until the journal’s retention trims them, exactly as for a job added with removeOnComplete. To drop older journal entries sooner, call queue.trimEvents(maxLength) (see Metrics).

// Remove completed jobs older than 1 hour, max 100 (async works in both modes)
const removed = await queue.cleanAsync(3600000, 100, 'completed');
// Promote delayed jobs to waiting now
const promoted = await queue.promoteJobs({ count: 50 });
// Re-queue failed jobs from the DLQ
await queue.retryJobs({ state: 'failed', count: 100 });
// Re-queue completed jobs through the same selector contract
await queue.retryJobs({
state: 'completed',
count: 100,
timestamp: Date.now() - 3600000, // completed at least one hour ago
});
// Direct completed-job helpers (e.g. after a logic change)
const count = await queue.retryCompletedAsync(); // all completed, use with care
const one = queue.retryCompleted('job-id-123'); // one job (sync, embedded; TCP returns 0)

Bulk retryJobs, retryCompleted, and counted promoteJobs are available in TypeScript and Python; PHP, Go, and Rust act per job (promote, retryJob); Elixir exposes an uncounted bulk promote_jobs/1.

For SQLite queues, completed cleanup queries the database rather than only the bounded in-memory cache. It removes the oldest eligible rows first with id as a deterministic tie-breaker, so repeated calls page through all retained history even when it exceeds maxCompletedJobs. The job, result, and related flow-failure rows are deleted in one transaction; the returned IDs are exactly the committed deletions. A completed dependency whose result is still needed by a live consumer is skipped until that consumer is removed or resolved.

Retrying a completed job starts a new waiting execution. Its previous returnvalue, progress/message, processedOn, and finishedOn are cleared; attempts restart at zero, while the diagnostic stacktrace and timeline history remain available. For persisted queues, that reset and removal of the old result are atomic in the selected backend: one SQLite transaction in single-broker mode, or a PostgreSQL transaction with its durable event in multi-broker mode. The cleared state therefore survives broker restart.

GuideWhat it covers
Queue APICreate a queue in embedded or TCP mode
Adding Jobsadd, addBulk, priorities, delays, durability
Deduplication and Idempotent Job AddsIdempotent adds, dedup keys, custom job ids
Querying JobsFetch jobs, states, counts and results
Progress, Job Logs and DependenciesProgress, per-job logs and dependencies
Queue Rate Limiting and Global ConcurrencyRate limits and global concurrency caps
Job Schedulers from the QueueNamed repeatable schedules from the queue
DLQ Operations from the Queue ObjectFailed-job operations from the Queue object
Workers, Stats and Metrics from the QueueRegistered workers, stats and metrics windows
Namespaces, Auto-Batching and Store-and-ForwardNamespaces, auto-batching, store-and-forward
JobOptions ReferenceEvery JobOptions field, with defaults