Skip to content
Get started
Get started
Worker Concurrency and Batch Pulling
View Markdown
guide · worker

More at once, fewer round-trips.

Concurrency decides how many jobs a single worker runs in parallel. Batch pulling decides how many it fetches per request. Together they set the ceiling on what one process can do.

const worker = new Worker('my-queue', processor, {
embedded: true,
concurrency: 5, // Up to 5 jobs at once (default: 1)
});

You can change concurrency at runtime, without restarting:

worker.concurrency = 10; // Scale up under load
worker.concurrency = 2; // Scale back down (minimum: 1)

Both TypeScript packages support runtime concurrency changes. The other SDKs fix concurrency at construction.

For high-volume queues, pull many jobs per round-trip and long-poll while idle:

const worker = new Worker('queue', processor, {
embedded: true,
batchSize: 100, // Request up to 100; free concurrency slots cap the pull
pollTimeout: 5000, // Wait up to 5s for jobs (long polling)
});

Bulk pushes wake idle long-polling workers immediately, they never wait out the timeout.

batchSize above optimizes transport but still invokes the processor once per job. The shared TypeScript batch option changes the processor contract: one call owns several independently leased jobs.

const worker = new Worker(
'webhooks',
async (leadingJob) => {
const jobs = leadingJob.getBatch?.() ?? [leadingJob];
for (const job of jobs) {
try {
await deliver(job.data);
} catch (error) {
job.setAsFailed?.(error instanceof Error ? error : new Error(String(error)));
}
}
return { received: jobs.length };
},
{
concurrency: 4,
batch: { size: 100, minSize: 10, timeout: 250, groupAffinity: true },
}
);

Here concurrency: 4 means up to four processor invocations, each with at most 100 jobs. size is 1..1000; minSize defaults to 1 and cannot exceed size. When a global Worker limiter is present, minSize also cannot exceed limiter.max; larger maximum batches remain valid and run in bounded chunks. With minSize and no positive timeout, the Worker waits indefinitely for the minimum. After a positive timeout it runs a partial batch. groupAffinity keeps every member on the same job-group ID. Without affinity, any batch that contains grouped jobs starts with the members already available instead of waiting for minSize.

A Worker limiter still counts job starts, not processor calls: a ready batch atomically consumes one slot per member, and a batch waiting for minSize consumes no slots. If the processor throws, it is invoked once and the same failure is applied to every member — including members already marked with setAsFailed(), whose per-member error only wins when the shared invocation resolves.

Cancellation is shared by the processor invocation. Cancelling or timing out any active member aborts the processor context signal, so cooperative processor code can stop the whole batch; every member then follows that shared outcome.

The processor’s return value completes every member not marked with setAsFailed(error). Each member still has its own token, events, retry budget, and final transition; one selective failure does not fail the rest of the batch. Both TypeScript packages support processor batches; the other SDKs retain transport batch pulling.

GuideWhat it covers
WorkerCreate a worker and process your first job
The Job Object Inside a Worker ProcessorEverything the processor receives and can do
Worker Eventscompleted, failed, stalled and the rest
Worker Error Handling, Retries and BackoffRetries, backoff, timeouts and giving up
Worker LifecyclePause, resume and shut down without losing work
Heartbeats, Stall Detection and Lock OwnershipHeartbeats, stall recovery and lock ownership
SandboxedWorkerExperimental isolation for CPU-heavy handlers
WorkerOptions ReferenceEvery WorkerOptions field, with defaults