Skip to content
Search lessons, topics, tests…
Esc

    ↑ ↓ moveEnter openEsc close

    Module 1 · Interview Method and the Request Path · Lesson 2 of 20

    Back-of-the-Envelope Estimation: QPS, Storage, Bandwidth and Latency

    Learning outcome

    You can turn "10 million daily users" into average and peak requests per second, a read/write ratio, storage growth per year, bandwidth, cache size and an instance count in about five minutes, out loud, with round numbers. You know the latency numbers every engineer should recognise, and you can use them to tell whether a design can meet its p99 target before you build it.

    Intuition

    Estimation is not about being exact. It is about knowing which order of magnitude you are in, because each order of magnitude needs a different design. Ten requests per second fits on one small app service. A thousand needs a few instances and a good database. A hundred thousand needs caching, replicas and probably partitioning. The estimate tells you which conversation to have.

    The trick is that a day has about 100,000 seconds (86,400). So "X per day" divided by 100,000 is "X per second", near enough. One million requests a day is about 10 per second (11.6 exactly). One billion a day is about 10,000 per second.

    flowchart LR
        U["Daily users x actions per user"] --> D["Requests per day"]
        D -->|"/ 86,400"| A["Average QPS"]
        A -->|"x peak factor (2-10)"| P["Peak QPS"]
        P --> I["Instances = peak / per-instance capacity + spare"]
        D --> S["Storage = writes per day x bytes x 365"]
        P --> B["Bandwidth = peak QPS x response size"]

    Deep dive

    The five numbers to produce

    1. Average QPS for reads and writes separately: daily actions / 86,400.
    2. Peak QPS: average times a peak factor. Consumer apps often see 2-3x at the daily peak and 5-10x or more at sales or launches. Ask, or state your assumption.
    3. Read/write ratio: it decides whether caching and replicas help (read-heavy) or whether you need write scaling (write-heavy).
    4. Storage growth: writes per day x bytes per record x 365, then multiply by replicas and backups. Include indexes; a rule of thumb is 1.5-2x the raw row size.
    5. Bandwidth: peak QPS x average response size. It drives CDN and egress cost.

    Powers of two and ten

    UnitApproximate value
    1 KB1,000 bytes (a small JSON object)
    1 MB1,000 KB (a photo)
    1 GB1,000 MB
    1 TB1,000 GB (a large single database)
    1 PB1,000 TB
    seconds per day86,400 (round to 100,000)
    seconds per monthabout 2.6 million
    seconds per yearabout 31.5 million

    Latency numbers worth knowing

    These are orders of magnitude, not benchmarks. They explain why caches, data locality and fewer round trips matter.

    OperationTypical latency
    L1 cache referenceabout 1 ns
    Main memory referenceabout 100 ns
    In-process dictionary or IMemoryCache lookupwell under 1 microsecond
    Read 1 MB sequentially from memoryabout 10-50 microseconds
    SSD random readabout 100 microseconds
    Round trip inside one datacenter / availability zoneabout 0.5 ms
    Redis GET over the network in the same regionabout 0.5-1 ms
    Simple indexed SQL query, same regionabout 1-5 ms
    Round trip between Azure regions on one continentabout 10-40 ms
    Round trip India to US Eastabout 200 ms

    Two consequences you can say in an interview:

    • Round trips dominate. An API that makes 30 sequential database calls at 2 ms each spends 60 ms before doing any work. Batch, join, or cache.
    • Cross-region calls belong off the hot path. A 200 ms round trip inside a request with a 300 ms p99 budget leaves almost nothing for the rest.

    Latency budgets

    Split the p99 target across the hops. For GET /orders/{id} with p99 < 300 ms: TLS and edge 20 ms, gateway 5 ms, API work 20 ms, cache 2 ms, database on a miss 20 ms, leaving a large margin. For POST /orders with a payment call that alone has p99 of 600 ms, the 800 ms budget is tight. That is the signal to make the email and the warehouse update asynchronous (Lesson 10).

    Capacity per instance

    Do not guess how many requests an ASP.NET Core instance can serve: it depends on what each request does. Say "I would load-test one instance at our target CPU (say 60%) and measure". For an I/O-bound JSON API that mostly waits on a database or cache, a few thousand requests per second per instance is common. Always add at least one spare instance so you survive a failure or a rolling deploy.

    Code example: an estimation calculator

    The program below encodes the estimate for the online shop from Lesson 1. Every constant at the top is an assumption you would say out loud.

    C#
    // Back-of-the-envelope estimate for a .NET shop API. Every input is an assumption you state out loud.
    const double dau = 10_000_000;          // daily active users
    const double readsPerUser = 30;         // product/catalog page API calls per user per day
    const double ordersPerUser = 0.2;       // one order every five days on average
    const double peakFactor = 3;            // evening/sale peak vs daily average
    const double orderBytes = 2_000;        // order row + lines + indexes, bytes
    const double readResponseBytes = 8_000; // average JSON response, bytes
    const double rpsPerInstance = 1_500;    // measured by a load test on one API instance at 60% CPU
    const double hotFraction = 0.2;         // share of products that serve most reads
    const double products = 2_000_000;
    const double productBytes = 4_000;
    
    const double secondsPerDay = 86_400;
    double readQps = dau * readsPerUser / secondsPerDay;
    double writeQps = dau * ordersPerUser / secondsPerDay;
    double peakRead = readQps * peakFactor, peakWrite = writeQps * peakFactor;
    double ordersPerYear = dau * ordersPerUser * 365;
    double storageYearGb = ordersPerYear * orderBytes / 1e9;
    double egressMbps = peakRead * readResponseBytes * 8 / 1e6;
    int instances = (int)Math.Ceiling(peakRead / rpsPerInstance) + 1;   // +1 so one instance can fail or be deployed
    double cacheGb = products * hotFraction * productBytes / 1e9;
    
    Console.WriteLine($"average read QPS     {readQps,12:N0}");
    Console.WriteLine($"average write QPS    {writeQps,12:N0}");
    Console.WriteLine($"peak read QPS (x{peakFactor})  {peakRead,12:N0}");
    Console.WriteLine($"peak write QPS (x{peakFactor}) {peakWrite,12:N0}");
    Console.WriteLine($"read:write ratio     {readQps / writeQps,12:N0} : 1");
    Console.WriteLine($"orders per year      {ordersPerYear,12:N0}");
    Console.WriteLine($"order storage / year {storageYearGb,12:N0} GB (before replicas and backups)");
    Console.WriteLine($"peak egress          {egressMbps,12:N0} Mbit/s");
    Console.WriteLine($"API instances        {instances,12} (peak / {rpsPerInstance:N0} rps, plus one spare)");
    Console.WriteLine($"hot product cache    {cacheGb,12:N1} GB");

    Output (dotnet run, .NET 10):

    Text
    average read QPS            3,472
    average write QPS              23
    peak read QPS (x3)        10,417
    peak write QPS (x3)           69
    read:write ratio              150 : 1
    orders per year       730,000,000
    order storage / year        1,460 GB (before replicas and backups)
    peak egress                   667 Mbit/s
    API instances                   8 (peak / 1,500 rps, plus one spare)
    hot product cache             1.6 GB

    What to notice:

    • 150:1 read/write. The system is read-heavy, so caching product and order reads gives the biggest win, and one primary database can easily take about 70 writes per second.
    • About 10,000 reads per second at peak. That is beyond what you want a single database to serve directly, so reads go through a cache (Lesson 8), and history reads can go to a replica (Lesson 6).
    • 1.5 TB of orders a year, before replicas and backups. Three years on one relational database is fine. If the interviewer says 10x, you are at 15 TB a year and partitioning (Lesson 7) or archiving old orders becomes part of the design.
    • 667 Mbit/s of egress at peak is a cost line and a reason to compress responses and serve images from a CDN.
    • 8 instances comes from a measured 1,500 requests per second per instance, plus one spare. Change the measured capacity and the answer changes, which is why you measure.
    • 1.6 GB of hot product data fits in one Redis node, or even in each instance's memory as an L1 cache.

    Failure modes

    • False precision. "3,472.2 requests per second" wastes time. Say "about 3,500, call it 4,000 with headroom".
    • Forgetting the peak. Systems fail at the peak, not on average. Always multiply.
    • Forgetting replicas, indexes and backups in storage. Raw rows are often less than half the real footprint.
    • Treating latency as additive only. Parallel calls cost the slowest call, not the sum. Sequential calls cost the sum. Say which one your design does.
    • Mixing up bits and bytes. Network bandwidth is quoted in bits per second; multiply bytes by 8.
    • Assuming capacity per instance. Measure with a load test; state the number as an assumption until you have.

    Interview angle

    Interviewers do not check your arithmetic to the decimal. They check that you produce the numbers, round sensibly, and use them. The pattern is: state the assumption, compute, then draw a conclusion. "10 million daily users, 30 reads each, is 300 million a day, about 3,500 per second on average and about 10,000 at a 3x peak. That is read-heavy at 150 to 1, so I will put a cache in front and keep writes on one primary."

    If you are given different numbers halfway through, redo the estimate quickly. Being willing to update the design when the numbers change is exactly what the interviewer is testing.

    Practice questions

    1. A service receives 50 million requests per day. What is the average QPS?

    Answer: About 580 per second (50,000,000 / 86,400). Call it 600, and plan for a peak of 1,500-3,000 depending on the traffic shape.

    1. Each order is 2 KB including indexes, and you store 1 million orders per day. How much storage per year?

    Answer: 2 GB per day, about 730 GB per year, before replicas and backups. With one replica and backups, plan for roughly 2 TB.

    1. A page makes 12 sequential calls to Redis at 1 ms each and 4 sequential SQL queries at 3 ms each. What is the data-access time, and how do you cut it?

    Answer: About 24 ms. Batch the Redis reads (MGET or a pipeline), run independent calls in parallel with Task.WhenAll, and combine queries. Parallel calls cost roughly the slowest one.

    1. Why add a spare instance to the computed instance count?

    Answer: So the system still meets its peak when one instance fails or is being redeployed. N+1 (or N+2 across zones) is the minimum for availability.

    1. The peak response is 8 KB and peak QPS is 10,000. What bandwidth do you need?

    Answer: 80 MB/s, which is 640 Mbit/s. Compression and a CDN for static content reduce it.

    Revision checklist

    • 86,400 seconds per day; 1 million per day is about 12 per second.
    • Peak = average x 2-10; state the factor.
    • Storage = writes x size x 365, then x replicas and backups.
    • Bandwidth in bits: bytes x 8.
    • Memory 100 ns, same-zone round trip 0.5 ms, cross-continent 100-200 ms.
    • Instance count comes from a load test, plus a spare.

    Analogy

    Everyday picture

    Planning a wedding

    You do not count every guest's appetite. You say "300 guests, about 3 plates each, so roughly 1,000 plates, and the rush is at 8 pm, so we need enough servers for half of them in an hour". Rough numbers decide whether you hire one caterer or three. Estimation in system design is the same: round numbers that tell you how big the kitchen must be.

    Quick reference

    Formulas

    • Average QPS = actions per day / 86,400.
    • Peak QPS = average x peak factor.
    • Storage per year = writes per day x bytes x 365 x (replicas + backups factor).
    • Bandwidth (bit/s) = peak QPS x response bytes x 8.
    • Instances = ceil(peak QPS / measured QPS per instance) + 1.
    • Cache size = hot items x item size.
    Sign in to mark lessons done and keep your place in the course.Sign in