Part 5 · 1 chapters · ~8 min

Background Workers

The worker pattern across runtimes (BullMQ, Celery, Sidekiq, Solid Queue, asynq, Spring Batch and @Async), enqueuing ids, retries with backoff, dead letters, scheduling with single-fire locks, and sizing workers separately from web.

7

One pattern, every language

runtimelibrarybackend
NodeBullMQRedis
PythonCelery (+ beat), RQ, DramatiqRedis or RabbitMQ
RubySidekiq; Solid Queue (Rails 8 default)Redis; the database
Goasynq, RiverRedis; Postgres
JavaSpring Batch, @Async with executors, JobRunr, Quartzdatabase, or a broker
ElixirObanPostgres
any, at scaleTemporal (durable workflows)its own cluster (course 22)
code
# Celery: an idempotent task with retries
@app.task(bind=True, autoretry_for=(TimeoutError,), retry_backoff=True, retry_jitter=True, max_retries=8)
def send_receipt(self, transfer_id):
    t = Transfer.objects.get(pk=transfer_id)
    if t.receipt_sent_at: return                          # idempotent: already done
    email.send(t.owner.email, render_receipt(t))
    Transfer.objects.filter(pk=t.pk, receipt_sent_at=None).update(receipt_sent_at=now())
BACKGROUND WORKERS
the same codebase, a different entry point, pulling jobs from a queue
enqueuepullweb processenqueue jobqueueRedis / RabbitMQ / SQSworker processperform jobretry with backoffdead letter queueschedulercron jobs
swipe the figure sideways, or tap expand for full screen
1/5
enqueue
The web process records the job (type, arguments, idempotency key) in the queue and returns. Arguments should be ids, not whole objects, so workers load fresh data.
enqueue ids, not objectsworkers load fresh state