Skip to content

Work queue pattern: spread jobs over workers

Thumbnail jobs shared by three workers: queue mode, one partition per job or per user, and leases that hand the work of a dead worker to the next one.

Updated View as Markdown

Many workers share one stream of jobs, and each job is done by exactly one of them: resizing photos, sending emails, rendering invoices. This is the work queue, also called competing consumers.

In Queen it is a consumer with no consumer group. Each message goes to one worker. Work runs in parallel across partitions: a partition is held by one worker at a time, so here every user’s photos are a partition, and three workers share four users.

Twelve photos in the queue thumbnails, in four partitions, one per user. Three workers with no consumer group compete for them: each takes one user's partition at a time, so w1 holds user-0, w2 user-1, w3 user-2, and user-3 waits for the first worker that is free. Each photo is resized exactly once.Twelve photos in the queue thumbnails, in four partitions, one per user. Three workers with no consumer group compete for them: each takes one user's partition at a time, so w1 holds user-0, w2 user-1, w3 user-2, and user-3 waits for the first worker that is free. Each photo is resized exactly once.
Competing consumers: each job once, one user's partition per worker at a time.
// Twelve photos to resize, from four users.
for (let i = 1; i <= 12; i++) {
  await queen.queue('thumbnails').partition(`user-${i % 4}`)
    .push([{ data: { photo: `p${i}.jpg` } }])
}

// Three workers compete for the jobs: each job goes to exactly one of them.
const worker = (name) => queen.queue('thumbnails')
  .partitions(1)                                  // one user's photos at a time
  .each()
  .idleMillis(1500)                               // stop when there is nothing left
  .consume(async (job) => resize(job.data.photo, name))

await Promise.all([worker('w1'), worker('w2'), worker('w3')])

All twelve photos were resized once each, shared out between the three workers (6, 3 and 3 in one run).

Why
order still holds inside each user’s photos, and a slow user holds up only its own partition. To go faster, add workers, up to the number of partitions that have work.

One thing to know: with a single partition the workers would take turns, one at a time. Give the jobs a partition per user, per customer or per tenant.

Built from: Pop and consume, Push.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close