retrystorm · Learn · Interview practice · Glossary · Components

Home › Learn › Scaling Out

Chapter 2 · Foundations · 12 min

Scaling Out

Capacity, queues, load balancers, and finding the next bottleneck.

Inkwell launches tomorrow. The press is coming, and the founders expect four times the usual traffic in the first minutes. One app server won't survive that.

Inkwell's backend works for a few hundred beta testers. Tomorrow the press arrives with thousands more. This chapter is about why designs stop working as traffic grows, and what to do about it.

Every server has a limit

An app server has a few CPU cores and a limited number of threads, slots for requests in progress. Each request needs some CPU time, and it holds a thread until it is completely done, including the time spent waiting for the database.

That gives two rough limits, and the lower one is the server's capacity:

  • CPU: cores × (1000 ÷ ms of CPU per request). A medium server has 4 cores and an Inkwell request needs about 3 ms of CPU, so 4 × (1000 / 3) is about 1 300 to 1 400 requests per second.
  • Threads: threads ÷ time each request holds one. This is Little's Law: requests in progress = arrival rate × time each one takes. With 50 threads and requests that take 20 ms, a server finishes at most 50 / 0.020 s = 2 500 requests per second.
LimitMedium app serverWhat it means
CPU cores4each request needs a slice of one
Threads50each held until the request is fully done
Waiting queue100requests beyond this are turned away

For Inkwell today the CPU runs out first. Threads run out first when requests spend a long time waiting on the database: at 100 ms each, 50 threads cover only 500 requests per second. A thread waiting is a thread not serving anyone else. In retrystorm, a component's Metrics tab shows each of these limits filling up during a run.

Utilization and queues

Utilization is how much of a limit is in use. When requests arrive faster than they can be handled, they wait in a queue. Waiting grows slowly at first, then sharply as utilization nears 100%:

Past 100% the queue fills up and new requests are turned away as errors. That's why nobody runs servers at 99%: you keep headroom for bursts like a launch.

Scale up or scale out

  • Scale up (vertical): a bigger machine with more cores. Simple, but machines only get so big, the biggest are expensive, and it's still one machine.
  • Scale out (horizontal): more copies of the server, called replicas. Because Inkwell's app servers are stateless, any replica can serve any request, so you can keep adding them.

Statelessness is what makes this work. If a server kept a user's login session in its own memory, every request from that user would have to return to the same server (sticky sessions), and a crash would log them out. Keeping sessions in a shared store, or in a signed token the client sends, lets any replica serve anyone.

Load balancers

With several replicas, something must decide which one handles each request: a load balancer. It sits in front of the replicas and spreads requests across them.

Any replica can serve any request, so the load balancer spreads them out.

Common strategies are round robin (take turns), least connections (send to the least busy replica), random, and consistent hash (the same key always goes to the same replica). In retrystorm, an app server with more than one replica must have a load balancer in front of it.

Bottlenecks move

Fix one limit and traffic flows further, until it hits the next one.

Scaling the app layer sent more traffic to the database, which became the new limit.

A database accepts only so many connections, open conversations with app servers, at once; when all of them are busy, new queries have to wait, and in retrystorm they're refused. A struggling app layer can hide that limit: relieve the app servers, and the database starts refusing connections instead. Scaling is a loop: find the resource that's full, relieve it, measure again.

In the wild

The ideas in this chapter, and where you'll meet them in real systems and interviews:

IdeaWhere you'll meet it
Load balancersAWS Elastic Load Balancing, NGINX, HAProxy, Envoy
Adding replicas automaticallyAWS Auto Scaling groups, the Kubernetes Horizontal Pod Autoscaler
Shared session storesRedis, or signed tokens such as JWTs

Your practice

Survive the Launch: traffic quadruples for ten seconds. Scale Inkwell's app layer out behind a load balancer, then find what breaks next.

Key takeaways

  • A server's capacity comes from its cores and threads, and every request holds a thread until it's done.
  • Waiting climbs sharply near 100% busy, so keep headroom for bursts.
  • Scaling up hits a ceiling; stateless replicas let you scale out.
  • A load balancer picks which replica handles each request.
  • Fix one limit and the next one shows up: find what's full, relieve it, measure again.

Read first

  • The Request Path

Practise it

  • Survive the Launch: Traffic quadruples at launch. Scale out, then find the next bottleneck.

Terms

  • Bottleneck: The resource that is full and limits the whole system. Relieve it and the next one appears.
  • Little's Law: Requests in progress equal the arrival rate times how long each one takes.
  • Load balancer: Sits in front of replicas and picks one for each request.
  • Scale out: Adding more copies of a server, called replicas (horizontal scaling).
  • Scale up: Moving to a bigger machine (vertical scaling).
  • Sticky sessions: Sending every request from a user to the same server, because that server holds their session.
  • Utilization: How much of a limit (CPU, threads, connections) is in use. Waiting grows sharply as it nears 100%.
AI system design mock interview · About · Contact · Privacy · Terms of use