Module 2 / One server, one database

Module 2 · lesson 8 of 9 · Read 9 min

Back-of-the-envelope numbers

Rough maths that tells you how big the design needs to be.

Before you choose between one server and fifty, you need to know roughly how much work there is. Not exactly. Roughly. Is it 10 requests a second, or 10,000? A gigabyte of data a year, or a terabyte a day? Those answers lead to completely different designs.

The tool for this is back-of-the-envelope estimation: quick, rough maths with round numbers, done in a few minutes. Its job is to tell you the size of the problem, so you neither build a spaceship for a bicycle ride nor a bicycle for a trip to the moon.

In this lesson we'll estimate Tiffin's load properly: requests, data and bandwidth.

Rules for rough maths

A few habits make estimation fast and good enough:

  • Round aggressively. A day has 86,400 seconds. Call it 100,000. You'll be off by 15%, which doesn't matter when you're trying to tell 10 from 10,000.
  • Write down every assumption. "Each customer makes 20 requests per session" is a guess. Writing it down lets others challenge it, and lets you fix one number later without redoing everything.
  • Always use units. "500" means nothing. 500 requests per second, 500 GB per year, 500 ms: those mean something.
  • Think about the peak, not just the average. Systems fall over at the busiest moment, not the average one.
  • Aim for the right power of ten. If your answer is between 200 and 800 requests a second, "a few hundred" is the useful conclusion.

An estimate isn't a prediction. It's a way to find out which order of magnitude you're designing for.

Numbers worth knowing

You need a feel for how fast things are and how much one machine can do. These are typical values, rounded:

Thing Rough number
Read from memory ~100 nanoseconds
Read 1 MB sequentially from memory ~50 microseconds
Random read from an SSD ~100 microseconds
Round trip inside one data centre ~0.5 ms
Simple indexed database query 1 to 5 ms
Round trip across a country 30 to 60 ms
Round trip to another continent 150 to 250 ms
One server, simple API requests 1,000 to 5,000 per second
One PostgreSQL server, simple queries 5,000 to 50,000 per second
One PostgreSQL server, writes 1,000 to 10,000+ per second
RAM on one large server 256 GB to several TB

These vary a lot with hardware and workload. But they're the right order of magnitude, and that's all we need.

And three conversions to keep handy:

  • 1 million per day ≈ 12 per second on average.
  • 1 KB x 1 million = 1 GB.
  • 1 request per second, all year ≈ 30 million requests.

Step 1: Who uses Tiffin, and how much?

Tiffin is in one city. Here are our assumptions:

Assumption Value
Customers who open the app on a typical day 150,000
Orders per day 50,000 (about 1 in 3 sessions orders)
API requests per customer session ~20 (restaurant list, a few menus, cart, checkout, status)
Share of the day's traffic in the 7pm to 10pm dinner rush 60%
Restaurants open, each with a kitchen tablet 800
Kitchen tablets poll for new orders Every 5 seconds
Riders on shift at peak 1,000
Rider app sends its location Every 10 seconds

Step 2: Requests per second

Do each client separately, at peak.

Customer app. 150,000 sessions x 20 requests = 3,000,000 requests a day. 60% of those, 1.8 million, land in the 3-hour dinner rush. 3 hours is about 10,800 seconds.

1,800,000 / 10,800 s ≈ 170 requests per second

Kitchen tablets. These don't care about dinner time; they poll steadily while the restaurant is open.

800 tablets / 5 s = 160 requests per second

Rider app.

1,000 riders / 10 s = 100 requests per second

Total at peak: 170 + 160 + 100 = 430. Call it ~500 requests per second.

Count only the customers and you'd say 170 a second. The tablets and the riders, which are easy to forget, push it to 430, more than two and a half times as much. Same app, more careful assumptions.

Look at the split. The kitchen tablets, which most people would forget, generate nearly as much traffic as all the customers. Over a whole day they generate more: 160 per second for 14 opening hours is about 8 million requests, against 3 million from customers. Estimation is often most useful for surprises like this. If we ever need to cut load, polling is the first place to look (Module 6 offers alternatives).

Step 3: Can one server handle it?

Say an average request uses 5 ms of CPU time on the server (parse, check, query, build JSON). One CPU core can then do:

1 s / 5 ms = 200 requests per second per core

An ordinary 8-core server can do about 1,600 requests a second. Our peak is about 500. So one server runs at around 30% CPU at the busiest moment of the day.

Conclusion: one server is enough, with room for about 3 times the traffic. That's comfortable for now, and tells us that if Tiffin triples, we'll need to think about more servers (Module 4).

What about the database? Each request does one to three queries, so at peak that's roughly 500 to 1,500 queries a second, mostly simple indexed reads. Writes are much rarer: each order creates a handful of rows (order, items, payment, status changes), so 3 orders a second is a few dozen writes, plus 100 rider location updates. A single PostgreSQL server handles that easily.

Step 4: How much data?

Now storage. Estimate the size of one record, then multiply.

Orders. An order row is about 250 bytes. With three items at ~100 bytes each, a payment row, and index overhead, call it ~1 KB per order, all in.

50,000 orders/day x 1 KB = 50 MB per day
50 MB x 365 ≈ 18 GB per year

Tiny. Ten years of orders fit comfortably on one server.

Menus. 800 restaurants x ~60 dishes x ~500 bytes ≈ 24 MB. Negligible.

Rider locations, if we keep every point:

1,000 riders x 1 point per 10 s x ~10 active hours/day
= 1,000 x 3,600 = 3.6 million points per day
3.6 million x 100 bytes ≈ 360 MB per day ≈ 130 GB per year

This is more than seven times the orders data, for information nobody will look at after the order is delivered (except perhaps for disputes). A good estimate leads straight to a design question: should we keep every location point at all? A sensible answer is to keep the latest location in the database, and the full trail only for a few weeks.

Photos. 800 restaurants x 60 dishes x a few sizes x ~100 KB ≈ 15 GB. That's large files that never change, so they belong in object storage (like Amazon S3), not in the database. The database stores only the URL.

Step 5: Bandwidth

Bandwidth matters most for things like images and video. A customer session might load 40 dish photos at ~30 KB each:

40 x 30 KB = 1.2 MB per session
150,000 sessions x 1.2 MB = 180 GB per day
At peak: 60% of 180 GB over 10,800 s ≈ 10 MB/s ≈ 80 Mbit/s

That's more traffic than all the API calls put together, served from one server's network card at dinner time. It's a strong hint that photos should be served from somewhere closer to users and built for big files. That's a CDN, which we'll meet in Module 3.

The summary that matters

After five minutes of maths, here's what we know:

Question Answer What it means
Peak requests ~500 per second One server is enough, with about 3x headroom
Biggest traffic source Kitchen tablet polling First place to look when cutting load
Database load ~1,500 queries per second, few writes One PostgreSQL server is fine
Order data ~18 GB a year No storage problem for years
Rider locations ~130 GB a year if all kept Don't keep them all
Photos ~15 GB, ~80 Mbit/s at peak Object storage and a CDN, not the database

Every row in that table turned into a design decision. That's the point of estimating.

Ten cities

Tiffin expands to 10 cities of similar size, so everything is about 10 times bigger. Estimate peak requests per second, order data per year, and rider location data per year (if all kept). Which of our "one server" conclusions still hold?

Show one answer

Peak requests: ~5,000 per second (assuming all cities share one dinner rush, which they do in one country). That's about three times what one 8-core server can do. We need more servers, or a much bigger one: Module 4.

Database queries: ~5,000 to 15,000 per second. Possible on one strong PostgreSQL server, but getting close to its limits. Caching (Module 3) and read replicas (Module 5) become important.

Order data: ~180 GB a year. Still fine on one database for years.

Rider locations: ~1.3 TB a year if all kept. Definitely not going into the main database. Keep only recent points, or move them to a store built for high write volume.

What to remember

  1. Estimation tells you the order of magnitude you're designing for. Round hard, write down assumptions, always use units.
  2. Design for the peak. For Tiffin, 60% of traffic lands in the 3-hour dinner rush.
  3. 1 million per day is about 12 per second. A simple request uses a few milliseconds of CPU, so one core handles about 200 a second.
  4. Count every client. Background polling can generate more traffic than the users you were thinking about.
  5. Estimate storage per record, then multiply. Big, fast-growing data (locations, photos) often belongs somewhere other than the main database.
  6. Every estimate should end in a decision: one server or many, keep or discard, database or object storage.