- Docs
- Worker
- Options Reference
Every knob, with its default.
The complete option surface for a Worker, what each one changes, and the defaults you get when you leave it alone.
Options reference
Section titled “Options reference”const worker = new Worker('queue', processor, { embedded: true, concurrency: 5, batchSize: 100, // Pull up to 100 jobs per request pollTimeout: 5000, // Long-poll: wait up to 5s for jobs instead of busy polling limiter: { max: 10, duration: 1000 }, // Max 10 jobs per second});const worker = new Worker('queue', processor, { embedded: false, concurrency: 5, batchSize: 100, // Pull up to 100 jobs per request pollTimeout: 5000, // Long-poll: wait up to 5s for jobs instead of busy polling limiter: { max: 10, duration: 1000 }, // Max 10 jobs per second});worker = Worker( "queue", processor, concurrency=5, batch_size=100, # Pull up to 100 jobs per request poll_timeout_ms=5000, # Long-poll: wait up to 5s for jobs (default) lock_ttl_ms=30000, # Job lease TTL ack_batch={"max_size": 50, "max_delay_ms": 5}, # Opt-in ACK batching)$worker = new Worker('queue', $processor, [ 'batchSize' => 100, // Pull up to 100 jobs per request 'pollTimeoutMs' => 5000, // Long-poll: wait up to 5s for jobs (default) 'lockTtlMs' => 30000, // Job lease TTL 'heartbeatIntervalS' => 10.0, // Heartbeats fire between jobs (sequential worker)]);worker := bunqueue.NewWorker("queue", processor, bunqueue.WorkerOptions{ Concurrency: 5, BatchSize: 100, // Pull up to 100 jobs per request PollTimeoutMs: 5000, // Long-poll: wait up to 5s for jobs (default) LockTtlMs: 30000, // Job lease TTL HeartbeatIntervalS: 10, // Heartbeats are disabled by default in Go})use std::time::Duration;use bunqueue_client::{Worker, WorkerOptions};
let worker = Worker::new("queue", processor, WorkerOptions { concurrency: 5, batch_size: 100, // Pull up to 100 jobs per request poll_timeout_ms: 5_000, // Long-poll: wait up to 5s for jobs (default) lock_ttl_ms: 30_000, // Job lease TTL heartbeat_interval: Some(Duration::from_secs(10)), ..Default::default()});worker = Bunqueue.Worker.new("queue", handler, concurrency: 5, batch_size: 100, # Pull up to 100 jobs per request poll_timeout: 5_000, # Long-poll: wait up to 5s for jobs lock_ttl: 30_000, # Job lease TTL heartbeat_interval: 10_000 )The table below documents the shared TypeScript Worker options for Bun, Node.js, and Deno. Options for the other SDKs are tabulated in the SDK guide.
| Option | Type | Default | Description |
|---|---|---|---|
embedded | boolean | false | Use in-process mode |
concurrency | number | 1 | Parallel job processing. A number > 0 (a fraction rounds up) or Infinity |
autorun | boolean | true | Start polling automatically |
heartbeatInterval | number | 10000 | Heartbeat interval in ms (0, a negative value or NaN = disabled). Any other value must be a finite number >= 1 |
batchSize | number | 10 | Jobs to pull per batch (max: 1000). A number > 0 (a fraction rounds up); larger values, Infinity included, are clamped to 1000 |
batch | { size, minSize?, timeout?, groupAffinity? } | - | Native batch processor. job.getBatch() exposes members and member.setAsFailed(error) selectively fails one job. minSize waits indefinitely without timeout; grouped batches require groupAffinity to wait. |
pollTimeout | number | 0 | Long-poll timeout in ms (max: 30000). A negative value or NaN means 0; larger values, Infinity included, are clamped to 30000 |
useLocks | boolean | true | Enable BullMQ-style job locks |
limiter | { max, duration, groupKey? } | - | Without groupKey: max job starts per rolling window, acquired atomically even with concurrent or manual processing. With groupKey: per-group concurrency cap of max (jobs grouped by job.data[groupKey], duration unused) |
group | { concurrency?, limit?: { max, duration } } | unlimited / none | Broker-authoritative per-job-group concurrency and fixed-window rate defaults. See Job Groups |
lockDuration | number | 30000 | Job lock TTL in ms. With useLocks, a finite number from 1 to Number.MAX_SAFE_INTEGER (the broker’s lease bound) |
maxStalledCount | number | 1 | Accepted for BullMQ compatibility, but not applied by the Worker. Configure the broker’s per-queue maxStalls policy with Queue.setStallConfig() or the HTTP API instead |
skipStalledCheck | boolean | false | In embedded or TCP mode, skip only this Worker’s subscription to stalled notifications. It does not disable broker-side stall detection or recovery |
skipLockRenewal | boolean | false | Suppress the per-job heartbeat timer entirely (no JobHeartbeat sent), so both lock renewal and broker-side stall freshness stop; only the worker-registration heartbeat keeps running |
drainDelay | number | 50 | Delay between polls when the queue is empty (ms). Without a long-poll, a finite number >= 1 |
removeOnComplete | boolean | number | KeepJobs | false | Auto-remove completed jobs. Only true is honored; number / { age?, count? } are accepted for BullMQ type compatibility but ignored — the job’s own removeOnComplete option still applies |
removeOnFail | boolean | number | KeepJobs | false | Same behavior as removeOnComplete: only true is honored, other values are ignored and the job-level removeOnFail option still applies |
connection | ConnectionOptions | - | TCP connection (host, port, token, poolSize) |
prefixKey | string | - | Namespace prefix; must match the producing Queue’s. See Namespace Isolation |
Connection pool sizing (TCP): when poolSize is not set, it defaults to min(concurrency, 8). Override it by setting poolSize explicitly.
| Embedded storage option | Type | Default | Description |
|---|---|---|---|
dataPath | string | Unset | SQLite path for the process-wide embedded manager. Use the same path as the producing Queue. A conflicting path throws; TCP storage is configured on the server. |
If dataPath is omitted, the Worker uses the existing embedded manager or the
configured data-path environment variables. With neither a path nor a configured
manager, embedded storage is memory-only. See Persistence.
Duration validation
Section titled “Duration validation”The constructor checks every duration before it starts a timer or contacts the
broker. Leaving an option out (or passing null) keeps its default. A numeric
string such as process.env.POLL_TIMEOUT ('1000') is read as that number; any
other value that is not a number throws a TypeError. A value the worker could not
use, such as NaN where a timer would run, Infinity (except for pollTimeout) or a
value below the minimum, throws a RangeError that names the option:
RangeError: Worker: drainDelay must be a finite number of milliseconds >= 1 (got 0)Long values are honoured exactly. For example, a heartbeatInterval of 30 days
sends its first heartbeat after 30 days, not after 1 ms.
| Option | Accepts |
|---|---|
heartbeatInterval | 0 (disabled; a negative value or NaN also disables) or a finite number of at least 1. A positive value below 1 would send a heartbeat about every millisecond |
pollTimeout | 0 or more, Infinity included; values above 30000 become 30000, and a negative value or NaN means 0 |
drainDelay | a finite number of at least 1, checked only when pollTimeout is 0 (the only mode that uses it). 0 would re-poll an empty queue without pause |
lockDuration | a finite number from 1 to Number.MAX_SAFE_INTEGER, checked only with useLocks (the default). A smaller value gives a lock that expires when it is granted; a larger one is refused by the broker |
concurrency and batchSize are counts. Each accepts a number greater than 0;
a fraction rounds up (concurrency: 2.5 runs up to 3 jobs at once), and
Infinity is accepted (concurrency: Infinity removes the limit; batchSize
above 1000 becomes 1000). 0, a negative value and NaN throw a RangeError,
because a worker with them never processed a job. Under a native batch, whose
size is the pull size, batchSize is not read. Assigning worker.concurrency
later follows the same rule, except that a value below 1 becomes 1.
worker.delay(ms, abortController?) resolves at once for 0 or a negative ms
and waits for any longer finite ms; NaN and Infinity reject its promise.
worker.rateLimit(ms) does nothing unless ms is a positive finite number (0, a
negative value, NaN, Infinity and null are no-ops). worker.extendJobLocks(ids, tokens, duration) needs a finite duration >= 1; an omitted or null duration
renews each lock with its own TTL. A numeric string is read as its number in all
three.
Some values only the broker can check: an invalid queue name, an option an older
broker rejects, or a missing or wrong auth token. When the broker refuses a pull for
such a reason, the worker retries with a backoff from 100 ms up to 30 s and reports
the refusal: as an error event with context: 'pull' when the worker has an
error listener, otherwise as one console line naming the queue and the reason
(at most once a minute). It never ends the process, and the worker resumes as soon
as the cause is fixed. A temporary refusal, such as the broker’s rate limit or a
lock timeout under heavy broker load, counts as an empty queue, as in earlier
releases: the worker polls again after drainDelay (10 ms with pollTimeout), with no
growing backoff, and reports nothing. An error listener that throws is logged, never rethrown.
worker.getNextJob() resolves undefined on a refusal and reports a permanent one the
same way.
A job’s own timeout is enforced by the broker. The worker aborts the
processor’s signal only when the broker enforces the timeout too. A timeout
of 0 means no timeout, so the job runs to completion. Producers accept any
finite timeout of 0 or more (one above about 136,900 years is clamped), as 2.9.10
did. A job that an older release or a direct broker call stored with another value
follows the same rule: NaN and Infinity mean no timeout, and a long timeout is
honoured exactly.
Native batch option details
Section titled “Native batch option details”batch.size must be an integer from 1 through 1000. minSize defaults to 1,
must be no larger than size, and waits indefinitely when timeout is omitted
or zero. With a global Worker limiter, minSize must also be no larger than
limiter.max; batch.size may be larger and is processed in bounded chunks.
A positive timeout allows the available partial batch to start after that many
milliseconds. groupAffinity: true makes each processor batch homogeneous by
group ID; without affinity, batches containing grouped jobs do not wait for
minSize.
With native batching, concurrency counts processor invocations rather than
individual batch members. The leading job exposes every member through
getBatch(), and setAsFailed(error) marks one member for its own failure and
retry transition. A Worker limiter counts every member and reserves the whole
batch atomically only when it is ready; waiting for minSize consumes no rate
slots. Cancelling or timing out any member aborts the one shared processor
signal. See Worker Concurrency and Batch Pulling.
Where to go next
Section titled “Where to go next”| Guide | What it covers |
|---|---|
| Worker | Create a worker and process your first job |
| Worker Concurrency and Batch Pulling | Run jobs in parallel and pull them in batches |
| The Job Object Inside a Worker Processor | Everything the processor receives and can do |
| Worker Events | completed, failed, stalled and the rest |
| Worker Error Handling, Retries and Backoff | Retries, backoff, timeouts and giving up |
| Worker Lifecycle | Pause, resume and shut down without losing work |
| Heartbeats, Stall Detection and Lock Ownership | Heartbeats, stall recovery and lock ownership |
| SandboxedWorker | Experimental isolation for CPU-heavy handlers |