Capabilities/Reliability & Infra

Job Queues & Background Workers

Async task processing with retries, dead-letter queues, and priority scheduling

queuesbackground-jobsasyncworkersretry

Learning lens

Start with judgment, not a universal prescription.

This page is a learning model for understanding the capability. It does not claim exact production internals, exact costs, or an infinite-scale architecture.

Evidence

Conceptual pattern

0->1 default

Start with a durable queue, clear ownership, retries, and idempotency before building a larger event platform.

Cost drivers

retry amplificationqueue requestsworker runtimedelivery history and logs

Watch for breakpoints

  • -backlog grows during normal traffic
  • -manual recovery becomes frequent
  • -independent consumers need replay

Learn first

Concepts behind this architecture

The Problem

Many operations are too slow or unreliable to execute during a user’s HTTP request — sending emails, processing images, generating reports, syncing data with third-party APIs, or running ML inference. Job queues decouple the request from the work: the API enqueues a job and responds immediately, while background workers process the job asynchronously. The challenge is ensuring jobs are processed reliably (at-least-once delivery), handling failures with intelligent retries, scaling workers independently, prioritizing urgent jobs, and monitoring the health of the entire pipeline.

Architectural Approaches

Redis-Based Queue (BullMQ, Sidekiq)

Use Redis as the job queue backend. Libraries like BullMQ (Node.js), Sidekiq (Ruby), or Celery (Python with Redis broker) provide job scheduling, retries, priority queues, and dashboards. Redis’s speed makes this ideal for high-throughput, low-latency job processing.

Pros

  • +Very fast enqueue/dequeue — Redis is in-memory
  • +Rich ecosystem — BullMQ, Sidekiq, Celery have mature features
  • +Built-in delayed jobs, rate limiting, and priority queues
  • +Easy to set up — Redis is already in most stacks

Cons

  • −Redis memory is expensive — not ideal for millions of queued jobs
  • −Durability risk — Redis can lose data on crash (mitigated with AOF)
  • −Scaling beyond single Redis instance requires Redis Cluster
  • −Not suitable for very long-running jobs (hours+)

Message Broker (RabbitMQ, SQS)

Use a dedicated message broker. RabbitMQ provides AMQP-based messaging with flexible routing, while AWS SQS offers a fully managed queue service. Better durability guarantees than Redis and purpose-built for reliable message delivery.

Pros

  • +Purpose-built for reliable message delivery
  • +Better durability — messages persisted to disk
  • +SQS: fully managed, scales automatically, pay-per-message
  • +RabbitMQ: flexible routing, exchanges, dead-letter queues

Cons

  • −Higher latency than Redis-based queues
  • −RabbitMQ: operational complexity (clustering, monitoring)
  • −SQS: limited features (no priority queues, FIFO limited to 300 msg/s)
  • −More infrastructure to manage (vs Redis you already have)

Event Streaming (Kafka)

Use Kafka as a durable, ordered log. Producers publish job events to topics, consumer groups process them. Kafka excels at high-throughput ordered processing but is heavier than needed for simple job queues.

Pros

  • +Extreme throughput — millions of messages per second
  • +Durable — replicated, persisted log with configurable retention
  • +Ordered processing per partition
  • +Replay capability — reprocess failed jobs from any offset

Cons

  • −Overkill for simple job queues — complex to operate
  • −No built-in delayed jobs, retries, or priority queues
  • −Consumer group rebalancing causes processing pauses
  • −Higher latency for individual job processing

Architecture

Loading diagram...
Job Queues & Background Workers — High-level architecture

Key Components

Job Producer

Any service that creates jobs. Enqueues a job with a type, payload, priority, and optional delay/schedule. Must generate an idempotent job ID to prevent duplicate processing. Should set a reasonable TTL so stale jobs don’t execute hours later.

Queue Manager

Manages multiple named queues with different priorities and processing characteristics. Routes jobs to the appropriate queue based on type. Handles delayed jobs (schedule for future execution) and recurring jobs (cron-like schedules).

Worker Pool

Fleet of worker processes that consume jobs from queues. Each worker pulls a job, processes it, and acknowledges completion. Supports concurrency limits per worker (process N jobs simultaneously). Can be specialized (image worker, email worker) or general-purpose.

Retry Manager

Handles job failures with configurable retry strategies: fixed delay, exponential backoff with jitter, or custom backoff functions. Tracks attempt count per job. After max retries, moves the job to the dead-letter queue for manual investigation.

Dead Letter Queue

Holds jobs that failed after all retry attempts. Provides a UI for inspecting failed job payloads, error messages, and stack traces. Supports manual retry (re-enqueue individual or batch) and permanent discard. Critical for debugging production issues.

Job Dashboard

Real-time visibility into queue health: pending count, processing rate, failure rate, average processing time, and worker utilization. BullMQ has Bull Board, Sidekiq has a built-in web UI. Alerts when queue depth exceeds thresholds.

Data Model

Loading diagram...
Job Queues & Background Workers — Entity relationship diagram

Retry Strategies and Backoff

Job failures are inevitable — network timeouts, rate limits, temporary outages. The retry strategy determines recovery speed and system impact.

Fixed interval: Retry every 30 seconds. Simple but can cause synchronized retries (thundering herd) if many jobs fail simultaneously.

Exponential backoff: 1s, 2s, 4s, 8s, 16s, … Spreads retries over time. Add jitter (random 0-50% of the delay) to prevent synchronized retries: delay = min(base * 2^attempt + random(0, base * 2^attempt * 0.5), max_delay)

Custom backoff: Different error types warrant different strategies:

  • Rate limited (429): Use the Retry-After header from the response
  • Network error: Exponential backoff (transient failure)
  • Validation error (400): Don’t retry — move to DLQ immediately
  • Internal error (500): Retry with exponential backoff

Max attempts: Typically 3-5 retries for API calls, 10+ for critical jobs. After max attempts, move to the dead-letter queue. Always cap the maximum delay (e.g., 1 hour) to prevent infinite backoff.

Idempotency: Since jobs may be processed more than once (at-least-once delivery), the job handler must be idempotent. Use unique job IDs and check for prior completion before executing side effects (sending emails, charging cards).

Scaling Workers

Worker scaling depends on the job type and processing characteristics.

CPU-bound jobs (image processing, PDF generation, ML inference): Scale by adding more worker processes/containers. Each worker runs one job at a time. Use container orchestration (Kubernetes HPA) to autoscale based on CPU utilization.

I/O-bound jobs (API calls, email sending, database writes): Increase concurrency per worker. A single Node.js worker with BullMQ can process 10-50 concurrent I/O-bound jobs. Scale based on queue depth rather than CPU.

Autoscaling formula: Target = ⌈queue_depth / (processing_rate × target_drain_time)⌉. If you have 10,000 pending jobs, each worker processes 100 jobs/minute, and you want to drain in 10 minutes: Target = ⌈10,000 / (100 × 10)⌉ = 10 workers.

Queue-based autoscaling: Monitor queue depth and processing latency. Scale up when queue depth exceeds threshold or processing latency exceeds SLA. Scale down when queues are empty. Use a cooldown period to avoid thrashing.

Graceful shutdown: When scaling down or deploying, workers must finish their current job before terminating. Implement SIGTERM handling: stop accepting new jobs, wait for in-progress jobs to complete (with a timeout), then exit. Kubernetes provides a terminationGracePeriodSeconds for this.

Job Scheduling and Cron

Beyond on-demand jobs, many systems need scheduled and recurring jobs.

Delayed jobs: Enqueue a job to be processed at a specific future time. Use case: “Send a follow-up email 24 hours after signup.” BullMQ and Sidekiq support this natively with a delay parameter.

Recurring jobs (cron): Jobs that run on a schedule — daily reports, hourly data sync, weekly cleanup. Implementation options:

  • In-queue scheduling: BullMQ’s repeat option creates jobs on a cron schedule. The queue library manages the schedule.
  • External scheduler: A cron service (node-cron, Kubernetes CronJobs) enqueues jobs at the scheduled time. Simpler but requires a separate scheduler process.

Distributed cron: In a multi-instance deployment, you must ensure the cron job is triggered exactly once, not once per instance. Solutions:

  • Use a distributed lock (Redis SETNX with TTL) before enqueuing
  • Use a single scheduler instance (leader election)
  • Use BullMQ’s built-in repeat — it handles deduplication automatically

Missed schedules: If the scheduler is down when a job should run, should it catch up? BullMQ skips missed runs by default. For critical jobs (daily billing), implement catch-up logic that checks the last run time on startup and enqueues missed jobs.

Real-World Examples

GitHub

Uses a custom job queue system for CI/CD (Actions), webhook delivery, and background processing. Jobs are distributed across thousands of workers. Implements priority queues for paid accounts and complex retry logic for webhook delivery.

Shopify

Processes billions of background jobs daily using a custom job framework built on Redis. Handles order processing, inventory updates, email sending, and storefront pre-rendering. Workers are autoscaled based on queue depth across multiple data centers.

Stripe

Event-driven architecture where payment events trigger chains of background jobs (send receipt, update ledger, trigger webhooks). Uses idempotency keys to ensure jobs are processed exactly once even with retries.

Vercel

Uses job queues for build processing and deployment. Each build is a job with resource limits and timeouts. Implements fair scheduling across customers to prevent one user’s builds from starving others.

Architectural Tradeoffs

Redis-based (BullMQ) vs managed service (SQS)

Pros

  • +Redis: fastest enqueue/dequeue, rich features (priority, delay, rate limit)
  • +SQS: fully managed, scales to any volume, pay-per-message
  • +Redis: familiar if already in your stack, great dashboard (Bull Board)

Cons

  • −Redis: memory-bound, durability concerns without AOF
  • −SQS: limited features (no native priority, max 14-day retention)
  • −SQS: higher per-message cost at very high volumes

At-least-once vs exactly-once processing

Pros

  • +At-least-once: simpler, most queue systems provide this by default
  • +Exactly-once: no duplicate side effects (critical for payments)
  • +At-least-once with idempotent handlers: practical compromise

Cons

  • −At-least-once: duplicate processing if worker crashes after processing but before ack
  • −Exactly-once: very complex, requires distributed transactions
  • −Idempotent handlers: more development effort per job type

Specialized vs general-purpose workers

Pros

  • +Specialized: optimized resource allocation (GPU for ML, high-memory for image)
  • +General-purpose: simpler deployment, all workers handle all jobs
  • +Specialized: can scale each job type independently

Cons

  • −Specialized: more deployment complexity, potential underutilization
  • −General-purpose: one job type can starve others
  • −Specialized: must maintain routing logic for job → worker type

Learn Job Queues & Background Workers

Copy a learning context prompt for an AI assistant. It keeps assumptions visible and asks for tradeoffs, cost drivers, failure modes, and what to measure before adding complexity.

Explore More Capabilities