Scalability for Dummies - Part 4: Asynchronism

By Oleksandr Andrushchenko — Published on — Modified on

In Scalability for Dummies - Part 3: Cache, we reduced database load by avoiding unnecessary queries.

But even after adding clones, optimizing the database, and introducing caching, some requests are still slow.

Why?

Because the user is waiting for work that does not need to happen right now.

Table of Contents

The Problem

Imagine a user uploads a profile picture.

The system needs to:

  • Store the file
  • Generate thumbnails
  • Compress the image
  • Update the database
  • Send notifications

Should the user wait for all of that?

Probably not.

The user only cares that the upload was accepted.

Everything else can happen later.

Why Users Wait

Many applications accidentally perform too much work during the request itself.

User Request
      │
      ▼
Database Update
      │
      ▼
Send Email
      │
      ▼
Generate PDF
      │
      ▼
Call Third-Party API
      │
      ▼
Return Response

The response becomes slower because every step must finish before the user receives an answer.

Urgent vs Non-Urgent Work

A useful question is:

Does the user need the result right now?

If the answer is no, that work is usually a candidate for asynchronous processing.

Operation Synchronous?
Login validation Yes
Payment authorization Usually yes
Sending email No
Thumbnail generation No
Analytics events No
Push notifications No

What Is Asynchronism?

Asynchronism means accepting work now and completing it later.

Instead of making the user wait, the application records a task and returns immediately.

User Request
      │
      ▼
Create Task
      │
      ▼
Return Response

Worker Processes Task Later

This dramatically improves perceived performance because users stop waiting for background work.

Real-World Analogy

Imagine ordering furniture online.

The website does not build the furniture while you wait.

It accepts the order, gives you confirmation, and schedules the work.

Asynchronous systems work the same way.

Queues and Workers

The most common implementation uses queues and workers.

User
  │
  ▼
API
  │
  ▼
Queue
  │
  ▼
Workers

The API remains fast because workers perform the expensive tasks separately.

If traffic doubles, you can add more workers without changing the API layer.

Scaling Workers

Suppose one worker processes 100 emails per minute.

Ten workers can process roughly 1,000 emails per minute.

This is the same scaling pattern we used with clones earlier in the series.

Architecture Example

User
  │
  ▼
API Clones
  │
  ▼
Redis / SQS / RabbitMQ
  │
  ▼
Worker Clones
  │
  ├── Send Emails
  ├── Generate PDFs
  ├── Resize Images
  └── Process Notifications

Notice that the user no longer waits for those tasks to complete.

Common Mistakes

Making Everything Async

Not every operation should be asynchronous.

If the user needs the result immediately, keep it synchronous.

Ignoring Retries

Workers fail.

Networks fail.

External APIs fail.

Background jobs should usually be retryable.

Ignoring Idempotency

A job may run twice.

Your system should produce the same result if that happens.

Conclusion

Asynchronism improves scalability by removing expensive work from the request path.

Instead of making users wait, systems accept work immediately and process it in the background.

This reduces latency, improves throughput, and allows workloads to scale independently.

If the user does not need the result right now, consider making it asynchronous.

In the next part of the series, we will explore another common scalability technique: batching and aggregation.

Comments (0)