design·lab

Real-world combination

Background Job Worker

Offload slow work onto a durable queue and a pool of workers so requests return instantly — and the work survives even if a process crashes.

✗ The problem

Slow work done inline blocks the request

Resizing an image, sending an email, generating a report — do it inline and the caller waits for all of it. Worse: if the process dies mid-way, the work is simply lost.

app.post('/upload', (req, res) => {
  resizeImage(req.file);   // 4s... blocks the request
  sendEmail(user.email);   // crash here? job is LOST
  res.send('OK');
});
Slow responses, request timeouts, and work that vanishes if the process crashes mid-way — the request thread and the job's fate are the same thing.
✓ The combination

Job (Command) → durable Queue → Worker pool

Wrap the task as a Command object, push it onto a durable queue, and let a pool of workers pull and run it asynchronously. The request just enqueues.

class ResizeImageJob {
  constructor(id) { this.id = id; }
  run() { resize(this.id); }
}

app.post('/upload', (req, res) => {
  queue.push(new ResizeImageJob(req.id));
  res.status(202).send();  // returns NOW
});
Request
POST /upload
Queue
durable
Worker pool
async
Failures retry, then land in a dead-letter queue; completion can notify interested parties (Observer).
✓ See it live

Enqueue instantly — workers process on their own clock

Submitting a job returns immediately. Click worker tick to make idle workers pull the next job and process it.

Queue
empty
Workers
👷
Worker 1
idle
👷
Worker 2
idle
👷
Worker 3
idle
queue: 0 · done: 0 · retried: 0 · dead-letter: 0

Deterministic: every 3rd job submitted (⚠) is marked to fail — watch it retry once, then dead-letter.

✓ Takeaway

Fast, resilient, and horizontally scalable

  • Fast responses: the request only enqueues — heavy work happens off the critical path.
  • Absorbs spikes: the queue buffers bursts instead of overloading the app server.
  • Durable + retryable: jobs survive crashes; failures retry, then dead-letter for inspection.
  • Scales horizontally: add more workers to drain the queue faster — no code changes.
  • Real uses: emails, thumbnails, exports, video transcoding — Sidekiq, Celery, BullMQ.
  • Caution: make jobs idempotent (retries may re-run them) and set sane visibility timeouts so a crashed worker's job gets picked up again.
🎯 Combines: Command (the job) + a queue + a worker pool (+ Observer on completion).