lumo

Module 01 · Lesson 6 of 14Free

Capacity estimation: users, storage and traffic

Level
Beginner
Topic
System design fundamentals
Reading time
14 min

When you finish this page you can

  1. Convert customer activity into average and peak request rates.
  2. Estimate CPU demand, requests in progress, bandwidth and storage with explicit units.
  3. Identify which assumptions could change a capacity decision.
  4. Turn an estimate into a focused measurement plan.

Builds on 2 lessons

Start with lesson 414 min
  1. Latency and throughput: measuring speed and capacity14 min
  2. Functional and non-functional requirements15 min

Video lesson · coming soon

At the festival sale, expects 1,000 requests a second. Its app server has 8 CPU cores. The launch checklist says “50,000 customers a day”, but that number doesn’t tell the team whether checkout will keep working.

How do you turn customers, clicks and saved orders into a useful capacity estimate?

Customers don’t arrive evenly

Capacity estimation means predicting resource demand from expected activity. It is a model with assumptions, not proof that a server can handle the load.

We stay at Kartly’s first stage: one app server and one PostgreSQL database. Start with the product targets from Functional and non-functional requirements. A useful estimate must support those targets, not just count requests.

Check yourselfAnswer in your head first, then reveal
Kartly has 50,000 customers a day. Would dividing that number by the seconds in a day tell you the server’s request rate?Show answerHide answer
No. That gives customers per second averaged across the day, not requests per second. Each customer can make many requests, and the sale concentrates arrivals into a short period.
Count activity first, preserve the peak separately, then multiply by the cost of doing the work.

For this lesson’s scenario, assume each daily customer generates 80 requests to Kartly’s app. This includes browsing, cart changes and checkout. It excludes image downloads, which we budget separately.

Count daily app requests

50,000 customers/day × 80 requests/customer = 4,000,000 requests/day

The customer count becomes useful only after we describe what customers do.

Find the daily average

4,000,000 requests/day ÷ 86,400 seconds/day ≈ 46 requests/second

Round this to about 50 requests a second for planning.

Preserve the sale peak

1,000 requests/second ÷ 50 requests/second = 20

The festival peak is about 20 times the rounded average. It is an independent scenario input, not something the daily average predicts.

Use seconds for server capacity, days for business activity, and years for retained data. Write the unit beside every input. Otherwise, a correct multiplication can answer the wrong question.

The average explains daily volume. The peak determines the next capacity question.

Size for the busy interval

A daily average spreads the festival rush across quiet hours. Customers cannot use spare capacity from midnight when checkout freezes at sale opening.

The interval matters too. A minute containing 60,000 requests averages 1,000 requests a second. It does not prove those requests arrived evenly within that minute.

Which traffic rate should Kartly use for launch planning?

Use the daily average.

This is easy to calculate, but it spreads the sale across the whole day. A plan based on about 50 requests a second ignores the expected launch rush.

Multiply the average by a large safety factor.

This covers more possible demand, but spends money on an unsupported assumption. It also does not explain which resource needs that extra capacity.

Why this choice

Kartly’s sale starts at a fixed time. That gives us a reason to expect a concentrated peak.

For this scenario, plan for the stated first-minute rate. Also test a brief burst above it. Choose that burst from launch evidence when available, rather than treating a universal multiplier as a rule.

For a slow-growing internal tool, observed busy-hour traffic may be enough. For a scheduled sale, shorter windows deserve attention.

We now know how much work arrives. We still need to know how expensive that work is.

Requests have different resource costs

A request rate is not a complete capacity description. Google’s overload guidance warns that different requests can require very different resources. It recommends considering the resources consumed, not only query counts.1

Use the distinction from Latency and throughput: time per request and requests per second describe different things.

For an initial calculation, assume the peak consists of requests like Kartly’s product-page example:

Input Scenario value
App CPU time per request 5 ms
Database wait 40 ms
Pricing-service wait 50 ms
Total server-side elapsed time 95 ms

These are illustrative request costs, not measured limits. The database and pricing service are existing dependencies, not new components introduced by this estimate.

Estimate app CPU demand

1,000 requests/second × 0.005 CPU-seconds/request = 5 CPU-seconds/second

This is about five fully occupied cores. Against eight available cores, the modeled request work consumes about 63% of total CPU capacity.

Estimate requests in progress

1,000 requests/second × 0.095 seconds/request = 95 requests

Under steady conditions, about 95 requests are in progress on average. This uses the relationship taught in Server , not a count of people using the site.

Server concurrency explains how waiting requests share workers. Here, the important distinction is between CPU work and elapsed time.

The database wait is not app CPU demand. It also does not reveal the database’s CPU cost.

Our estimate therefore says “the modeled app CPU demand fits”. It does not say “the system handles the sale”.

For a real request mix, calculate each request class separately, then add its demand. If checkout costs more CPU than browsing, changing their proportions changes total demand even at the same request rate.1

The missing measurements are now specific: request mix, dependency capacity and behavior under load.

Bytes expose a different limit

For this scenario, assume an average app response body of 20 kB. We use decimal units throughout: one kB is 1,000 bytes; one GB is one billion bytes.

Estimate peak response traffic

1,000 responses/second × 20,000 bytes/response = 20 MB/second

This is the app’s outgoing response-body traffic under the stated assumptions.

Convert bytes to bits

20 MB/second × 8 bits/byte = 160 Mb/second

Compare bits per second with network capacity. Do not compare 20 MB/second directly with a link advertised in Mb/second.

This is a payload estimate. It excludes request bodies, protocol overhead and images. Compare it with usable network measured under the intended conditions, not just a connection’s advertised rate.

Now suppose each page also downloads three images. At an assumed 200 kB per image, that is 600 kB per page.

Do not multiply that by every app request. Cart updates and checkout calls are not page loads. First estimate image-bearing page views, then multiply by image bytes.

That separation prevents two mistakes: overlooking media traffic and charging every request for media it never downloads.

Storage needs the same separation between different kinds of data.

Retention turns writes into storage

Kartly takes 5,000 orders a day. For this scenario, assume each order and its line items need 2 kB of logical data.

Logical data means the estimated content before database storage overhead. PostgreSQL stores table data and indexes separately; wide values can also use additional storage. A payload estimate therefore does not equal physical disk usage.2

Estimate daily order growth

5,000 orders/day × 2,000 bytes/order = 10 MB/day

This counts newly created order data. It does not count every update as another complete order.

Choose a retention window

10 MB/day × 365 days = 3.65 GB

At an unchanged order rate, one year adds about 3.7 GB of logical order data.

Add an explicit planning allowance

3.65 GB logical data + 3.65 GB assumed overhead = 7.3 GB

For this first budget, allow an equal amount for indexes and storage overhead. This doubling is an assumption to replace with measurements, not a PostgreSQL rule.

Diagram not drawn yet: capacity-storage-ledger.svg

A storage ledger separates 3.65 GB of annual logical order growth from an assumed 3.65 GB allowance for indexes and overhead, totaling 7.3 GB of estimated physical growth. Separate unpriced rows name existing data, operational free space and backups. A media panel calculates 100,000 products times three images times 200 kB as 60 GB of image content.

Estimate each storage category separately. Order growth alone is not the database size or the total storage budget.

The 7.3 GB estimate is annual growth, not a disk purchase recommendation. Existing tables, logs, temporary work and operational free space still need budgets. Backups need a separate budget too.

For comparison, Kartly’s 100,000 products would contain 60 GB of image content under our three-image assumption. That is a catalog snapshot, not annual growth.

Replace the database allowance by measuring representative tables and their indexes. PostgreSQL documents SQL functions as its recommended way to inspect disk usage.2

The storage result gives us a reason to measure actual bytes. It does not yet justify splitting the database.

Uncertainty should change the next action

Suppose Kartly’s team proposes a 70% app CPU planning budget. That leaves headroom: capacity deliberately left unused in the model.

Calculate the chosen CPU budget

8 cores × 0.70 = 5.6 CPU-seconds/second

This is a scenario policy, not a universal safe threshold.

Compare modeled demand

5 CPU-seconds/second ÷ 5.6 CPU-seconds/second ≈ 89%

The estimated request work consumes most of that chosen budget. Unmeasured overhead could change the decision.

Should Kartly buy more app capacity now?

Increase app capacity immediately.

This buys potential CPU room, but may leave the actual limit unchanged if the database or pricing service saturates first.

Why this choice

The estimate identifies app CPU as worth checking. It does not identify it as the proven .

For this scenario, assume the team has time to test before launch. Keep the simple deployment while gathering evidence.

The choice changes if measured demand exceeds the budget, or procurement takes longer than the time remaining. In that case, reserve capacity earlier and validate the complete .

A small sensitivity check helps prioritize that test:

Changed assumption Derived consequence
App CPU cost doubles to 10 ms Peak request work needs 10 cores
Response bodies double to 40 kB Peak response-body traffic reaches 320 Mb/second
Order rate doubles for a full year Logical annual order growth reaches 7.3 GB

Each row changes one input. That makes the cause visible.

Measurements close the planning loop

Run the expected mix against representative data. Reproduce the sudden sale opening, rather than only increasing traffic slowly.

Record incoming requests, completed requests, errors and . Also inspect CPU, memory, waiting work and dependency limits. Resource consumption matters because equal request rates can impose different costs.1

For an illustrative failure, suppose 1,000 requests arrive each second but only 800 complete. If none are rejected or abandoned, waiting work grows by 200 requests a second. Customers see increasingly delayed cart and checkout responses.

More waiting is not extra capacity. Compare arrivals with completions to detect the gap, then identify the saturated resource. The load-testing lab develops that measurement process.

Where this shows up

Enough capacity under normal conditions still leaves another question: what happens when a component fails? Next, Core system properties separates serving requests, preserving data and agreeing on values.

Common mistakes

  • Starting from registered accounts. Use active customers and their activity for the period you are estimating.
  • Treating a request rate as a benchmark. State the request mix, data size and acceptable latency alongside it.
  • Counting requests as writes. A read may store nothing; an order can create several records.
  • Buying exactly the calculated storage. Separate growth, existing data, working space and backups.
  • Hiding uncertainty in precise numbers. Keep the arithmetic inspectable, then round the result and name the uncertain inputs.

Check your understanding

Check yourselfAnswer in your head first, then reveal
Kartly’s customer count stays unchanged, but app requests per customer double. What happens to daily app request volume?Show answerHide answer
It doubles because volume is customers multiplied by requests per customer. Customer growth is not the only source of traffic growth.
Check yourselfAnswer in your head first, then reveal
At 1,000 requests a second, app CPU cost rises from 5 ms to 10 ms per request. Can eight cores supply the modeled CPU demand?Show answerHide answer
No. The modeled work needs 10 CPU-seconds each second, more than eight cores can supply. More waiting workers cannot create the missing CPU capacity.
Check yourselfAnswer in your head first, then reveal
A response averages 20 kB. Does a measured rate of 160 Mb/second prove that 1,000 requests a second will meet the latency target?Show answerHide answer
No. That arithmetic covers only response-body traffic under the stated assumptions. CPU, dependencies, overhead or waiting work could still prevent the latency target.
Check yourselfAnswer in your head first, then reveal
Your estimate gives 3.65 GB of annual logical order growth. Would a 4 GB disk be an adequate purchase?Show answerHide answer
Not from that estimate. It excludes existing data, indexes, other storage overhead and operational free space; backups also need a separate budget.

Summary

  1. Turn customers into activity counts, preserve peak windows, then multiply by the resource cost of each activity.
  2. Keep units visible: requests per second, CPU-seconds, bytes per second and bytes retained.
  3. Estimate CPU work separately from elapsed time and dependency demand.
  4. Separate logical storage growth from physical storage, free space, media and backups.
  5. Treat uncertain inputs as measurement priorities. An estimate guides a load test; it does not replace one.

Report a mistake

Tell us what’s wrong in this lesson and we’ll fix it.