Skip to content
Get started
Get started
Progress, Job Logs and Dependencies
View Markdown
guide · queue

What a job is doing right now.

A job that runs for ten minutes should not be a black box. Progress and logs make it observable, and dependency links show how it relates to the rest of a graph.

// Progress and logs (also available on the job object inside a processor)
await queue.updateJobProgress('job-id', 75);
await queue.addJobLog('job-id', 'Processing step 3 completed');
const { logs, count } = await queue.getJobLogs('job-id', 0, 100);
// Parent/child flows (see the Flow guide)
const childValues = await queue.getChildrenValues('parent-job-id');
const deps = await queue.getJobDependencies('job-id');
const processed = await queue.getDependencies('parent-id', 'processed', 0, 10);
// Wait for a job to finish (requires a QueueEvents instance)
import { QueueEvents } from 'bunqueue/client';
const queueEvents = new QueueEvents('my-queue', {
embedded: false,
connection: { host: '127.0.0.1', port: 6789 },
});
await queueEvents.waitUntilReady();
const result = await queue.waitJobUntilFinished('job-id', queueEvents, 30000);

getJobDependencies, getDependencies, and QueueEvents are available in both TypeScript packages. Bun QueueEvents supports both embedded and TCP brokers; use the same connection and prefixKey as the Queue. External SDKs expose waitForJob for the common wait-for-result case and can use SSE/WebSocket for live queue events.

A wait settles on the job’s final outcome, as in BullMQ: it resolves with the result once the job completes, even on a later attempt, and rejects with the last attempt’s error once no retry is left, including a job the stall detector moved to the dead-letter queue. A failed attempt that will be retried does not end the wait; on QueueEvents its failed event carries terminal: false. job.waitUntilFinished(queueEvents, ttl) behaves the same, and also accepts null instead of QueueEvents, waiting up to ttl (30 seconds by default). A ttl of 0 means no timeout, with or without QueueEvents.

A job that no longer exists (removed, drained, obliterated) rejects the wait with Job <id> not found: its outcome is unknown. A job removed on completion (removeOnComplete) is the exception while the broker still keeps its result (see below). A wait survives a QueueEvents that closes or reconnects and a broker outage (rate-limit refusals, timeouts and lost connections are retried, not reported); only its ttl or a final error such as a refused token ends it early. After an outage it settles once the client has reconnected, which can lag by the reconnect backoff (up to 30 seconds) plus the next state read.

Request/reply with removeOnComplete: true works with or without QueueEvents. The broker keeps the result of a job removed on completion for a short, bounded time, in memory only: 60 seconds and the last 1,000 such jobs by default (removedResultRetentionMs / maxRemovedResults, see Server configuration). A wait that reaches the broker after the job completed, because a fast worker finished before the first WaitJob hold arrived or because the wait started after the completed event, settles on that result instead of rejecting with Job <id> not found. Read the result within that window: after it, after job.remove(), after the queue is obliterated, once a new job reuses the same custom ID, or after a broker restart, the job reads as not found. PostgreSQL keeps its own completion record with the result. getJobState() still reports unknown and the stored-result read (GET /jobs/:id/result, TCP GetResult) stays empty for such a job; only the wait reads the retained result.

QueueEvents remains the best path for high-concurrency request/response: its completed event delivers the result to every wait at once, without the per-connection limit on WaitJob holds described below.

Over TCP without QueueEvents, each connection of the pool holds the broker’s WaitJob for at most 40 waits at a time (160 on the default pool of 4), so the broker keeps slots for other commands. A wait with a slot sees its job complete at once; waits beyond that queue for a slot and can see a completion seconds late (about 2 seconds at p90 with 1,000 concurrent waits on 1-second jobs). A failure is reported by the next state read: about a second after the wait starts and at most about 37 seconds late after that, while no more than about 600 waits share the pool (later beyond that). For high-concurrency request/response, pass a QueueEvents: it reports every completion and failure as it happens.

Manual state transitions are BullMQ-compatible. Whenever the active job has a lock, token is mandatory and must be that job’s current worker token in both embedded and TCP mode. Jobs processed without locks remain administratively movable without one:

await queue.moveJobToCompleted('job-id', { success: true }, token);
await queue.moveJobToFailed('job-id', new Error('reason'), token);
await queue.moveJobToWait('job-id', token);
await queue.moveJobToDelayed('job-id', Date.now() + 60000, token);
await queue.moveJobToWaitingChildren('job-id', token);

The full token-bound transition set is available in both TypeScript packages. Python supports the transitions except moveJobToWaitingChildren; PHP and Go expose moveJobToFailed, Elixir exposes move_to_delayed and retry_job, and Rust intentionally leaves completion/failure acknowledgements to Worker while exposing queue-level retry.

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
Queue Control and MaintenancePause, drain, obliterate, clean and repair
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