Why Go Async? Background Jobs & Task Queues
You'll learn to
- -Recognize work that does not need to finish before the user gets a response
- -Explain how a queue and a pool of workers keep slow work off the request path
- -Explain scheduled tasks and how they share the same worker queue
- -Explain backpressure, and the load-shedding strategies that keep a growing queue under control
A web server's job is to answer quickly. A request that triggers something slow, such as encoding a video, generating a PDF report or sending a batch of emails, ties up a server thread for seconds while the user, and everyone queued behind them, waits. The numbers show how badly. Say a server has 50 threads and each request holds one. If a request takes 50 milliseconds, the server can finish 50 / 0.05 = 1,000 requests per second. If every request takes 5 seconds, it can finish only 50 / 5 = 10. The fix is to stop doing slow work inline.
An Analogy: The Restaurant Buzzer
At a busy counter-service restaurant you order, pay, and are handed a buzzer. The cashier does not cook your meal before serving the next customer, which would stretch the line out of the door. The order goes onto a rail, the kitchen works through it at its own pace, and the buzzer tells you when it is ready.
- -The cashier is the web server: it takes the request and answers quickly.
- -The order ticket on the rail is a message in a queue.
- -The kitchen is the pool of workers, which pull tickets off the rail independently.
- -The buzzer is how the user finds out the result: a notification, or a status they can check.
Background Jobs
The web server's job becomes: accept the request, hand the slow work to a queue, and immediately respond that it has been accepted. A separate pool of workers pulls tasks off the queue and processes them at their own pace, without holding up any user-facing request. The response is usually HTTP 202 Accepted, often with a job id the user can use to check progress. The diagram shows the flow:
The card below describes the Worker component as it appears in the lab:
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
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.
The card below describes the scheduler component:
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
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.
Here is the arithmetic. Suppose producers add 120 jobs a second and the workers finish 100 a second. The queue grows by 20 a second, which is 72,000 jobs after an hour. A job added at that moment has 72,000 jobs in front of it, and at 100 a second it waits 720 seconds, twelve minutes, before a worker even starts on it. Nothing crashed, yet the system is effectively broken. The simulation below lets you set the arrival and processing rates and watch the queue grow or stay healthy.
Queue Depth Under Backpressure
A worker pool serving 5 requests per tick, a queue capped at 20. Watch what happens when arrivals outpace that.
tick
0
arrivals
4
queue depth
0
shed so far
0
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.
Discussion0
Join the Discussion
Sign in to leave comments, reply to others, or like insights.
No comments yet. Be the first to start the thread!