Skip to content
HLD Learn/Asynchronous Systems
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Why Go Async? Background Jobs & Task Queues

6 min read

You'll learn to

  • -Recognize workloads that don't need an instant response
  • -Explain backpressure and the load-shedding strategies that handle it

Recall from Module 2 that a web server thread handling a request should stay fast and stateless. A request that triggers something slow (encoding a video, generating a PDF report, sending a batch of emails) blocks that thread for seconds while the user (and every other request queued behind it) waits. The fix is to stop doing slow work inline.

Background Jobs

The web server's job becomes: accept the request, hand the slow work off to a queue, and immediately respond "got it, we're on it." A separate pool of workers pulls tasks off that queue and processes them independently, at their own pace, without holding up any user-facing request.

ComponentWorker

A worker is a background helper that handles jobs that would take too long to do while a user is waiting. Imagine you're at a photo printing kiosk. You upload 50 photos and hit 'Print.' The kiosk doesn't make you stand there for 20 minutes while it prints. It says 'Got it! We'll notify you when your prints are ready' and you walk away. Meanwhile, a worker in the back room picks up your print job from a queue and processes it. In software, workers do the same: they pull tasks from a message queue (like SQS or Redis) and process them in the background. Sending emails, resizing images, generating PDF reports, syncing data to another system, running AI models. The web server's only job is to say 'Task received!' and move on to serve the next request immediately.

Examples: Celery (Python), Sidekiq (Ruby), Bull/BullMQ (Node.js), AWS Lambda (event-driven), AWS SQS + ECS workers

ComponentTaskScheduler

A task scheduler is your system's alarm clock. Just like you set an alarm for 7:00 AM to wake up, a task scheduler runs specific jobs at specific times or intervals. 'Every night at 2:00 AM, clean up expired sessions from the database.' 'Every Monday at 6:00 AM, generate the weekly sales report and email it to the CEO.' 'Every 5 minutes, check if any premium subscriptions have expired.' These jobs happen automatically, on schedule, without any human or user triggering them. It's the invisible maintenance crew that keeps your system healthy while everyone is sleeping.

Examples: AWS EventBridge Scheduler, Kubernetes CronJob, Celery Beat (Python), Quartz Scheduler (Java), node-cron, GitHub Actions scheduled workflows

Client
Web Server
Message Queue
Worker
Web ServerMessage Queue- enqueue, respond immediatelyMessage QueueWorker- process later

Scheduled Tasks

Not every background job is triggered by a user request. Some work needs to run on the clock regardless of what anyone does: regenerating a recommendation cache overnight, sending a weekly billing summary, purging sessions that expired hours ago. A task scheduler (cron, or a managed equivalent like AWS EventBridge) fires these jobs at fixed intervals and drops them onto the same worker queue as any request-triggered job, so a worker never needs to know or care whether a task exists because a user asked for something or because the clock struck midnight.

Backpressure: When the Queue Grows Faster Than Workers Can Drain It

Handing work off to a queue does not make capacity problems disappear, it just moves them. If producers enqueue work faster than the worker pool can process it, the queue keeps growing. An unbounded queue turns that growth into an unbounded memory leak and, since work is now sitting for longer and longer before a worker ever reaches it, a slowly climbing effective latency for anyone waiting on the result. Backpressure is the general term for a system pushing back on producers once it cannot keep up, instead of silently absorbing unlimited work and hoping capacity catches up later.

Queue Depth Under Backpressure

A worker pool serving 5 requests per tick, a queue capped at 20. Watch what happens when arrivals outpace that.

capacity (20)

tick

0

arrivals

4

queue depth

0

shed so far

0

tick 0 / 29

A bounded queue is the first piece: capping how much work can sit waiting turns an unbounded memory problem into a bounded, predictable one. Once that cap is reached, the system has to decide what to do with the next arrival, and that decision is called load shedding.

  • -Reject new work (fail fast): return an explicit error, commonly a 429 Too Many Requests, so the caller knows immediately to retry later or back off, rather than waiting on a request that was going to time out anyway.
  • -Drop the oldest queued item: useful when only the freshest data actually matters, like a live metrics feed where a stale reading is worse than a missing one.
  • -Signal the producer to slow down directly: TCP itself does this at the transport layer, and some queue protocols expose an explicit "pause sending" signal so a well-behaved producer throttles itself instead of being rejected.

An unbounded queue is rarely the safe default it looks like. It just delays the failure and makes it worse: instead of a fast, explicit rejection the moment capacity is exceeded, the system quietly accumulates a growing backlog until it runs out of memory or every request has waited so long that timing out is the only outcome left.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.