“How many servers for Diwali?”
Kabir is preparing for TiffinGo’s full-time interview, which follows the internship. A senior shared the question they always ask: “We expect 10 crore orders on Diwali day. How much storage and how many servers do we need?” Kabir freezes. He doesn’t know the exact answer.
Sir, how can anyone answer that without data? I'd need weeks of measurements.
Nobody expects the exact number, Kabir. They want to see you estimate: round numbers, clear assumptions, and arithmetic you can do on paper. Engineers call it a back-of-envelope estimate.
Like guessing how many samosas the canteen needs for the college fest?
Exactly that. How many students, how many will come, how many samosas each, plus some extra. Here: how many orders, how fast at the peak, how big each one is, how much one machine can handle.
And once we know we need more than one machine?
Then you choose between a bigger machine, scaling up, and more machines, scaling out. You met both in Lesson 1.2. Today we go deeper: why some services scale out easily and others don’t, how autoscaling follows the Diwali curve, and why adding machines can sometimes make a system slower.
Scalability and back-of-envelope estimation
Scalability is the ability of a system to handle increasing load by adding resources while maintaining acceptable performance. Vertical scaling (scaling up) increases the capacity of a single machine; horizontal scaling (scaling out) adds more machines and distributes load among them, typically behind a load balancer. A back-of-envelope estimate is a quick, approximate calculation of load, storage, bandwidth and machine count using rounded numbers and explicit assumptions, used to judge the feasibility and scale of a design.
Scaling means keeping a system fast as more people use it. You can buy a bigger machine (scale up) or add more machines (scale out). A back-of-envelope estimate is quick, rough arithmetic with round numbers that tells you roughly how big the system must be.
Words you’ll meet in this lesson
Numbers for quick estimates
| Seconds in a day | 86,400 ≈ 1 lakh (105) · seconds in a year ≈ 3 crore (3 × 107) |
|---|---|
| Indian units | 1 lakh = 105 · 1 crore = 107 · 10 lakh = 1 million · 100 crore = 1 billion |
| Sizes | 1 crore × 1 KB ≈ 10 GB · 1 crore × 1 MB ≈ 10 TB |
| Bandwidth | 1 Gbps ≈ 125 MB/s ≈ 10 TB per day if fully used |
| Typical record sizes | payment or order row: 0.25–1 KB · app event: 0.5–2 KB · photo: 2–5 MB |
| Safe target load | plan for 50–70% utilisation at peak |
Scaling and estimation at three levels
Beginner A bigger jug or more stalls
- Scaling up (vertical): the lemonade stall buys a bigger jug and a faster juicer. Simple, but there is a biggest jug, and if the one stall closes, nobody gets lemonade.
- Scaling out (horizontal): open more stalls and send customers to whichever is free. Needs a person at the gate (a load balancer), but you can keep adding stalls.
- Estimation recipe: (1) how many things per day, (2) divide by about 1 lakh seconds for “per second”, (3) multiply by a peak factor, (4) multiply by size for storage, (5) divide by what one machine handles for machine count.
Handy facts: a day has 86,400 seconds, call it 1 lakh. 1 crore = 107. 1 KB × 1 crore ≈ 10 GB.
Intermediate Stateless services, load balancers and autoscaling
- Stateless vs stateful: a stateless service keeps nothing between requests (any server can answer any request), so it scales out by adding copies. A stateful one (a database, a cache, a session held in memory) must split or copy its data first, which is much harder.
- Move state out: keep sessions in Redis and data in databases, so web and API servers stay stateless.
- Load balancer: spreads requests across servers (round robin, least connections) and stops sending traffic to unhealthy ones.
- Scaling databases: read replicas for read-heavy traffic, then sharding (splitting data across machines, Lesson 3.2) when writes outgrow one machine.
- Autoscaling: add or remove machines based on a metric (CPU above 60%, queue length, requests per second), with a minimum and maximum. Scale up early for known peaks; new machines take minutes to start.
- Headroom: plan to run at 50–70% of capacity, so a spike or a failed machine doesn’t push you over the edge.
Advanced Why scaling out has limits
- Universal Scalability Law (Neil Gunther): throughput with N machines ≈ N ÷ (1 + α(N−1) + βN(N−1)). α is contention (waiting for a shared resource, like Amdahl’s serial part); β is coherence (machines talking to each other to stay in agreement). With β > 0, adding machines beyond a point reduces throughput.
- Little’s law: items in the system = arrival rate × time each spends inside (L = λW). At 5,000 requests/s and 200 ms each, about 1,000 requests are in flight at any moment, which sizes thread pools and connections.
- Tail latency at scale: if one request fans out to 100 servers and each is slow 1% of the time, 1 − 0.99100 ≈ 63% of requests hit at least one slow server. Large systems watch p99 latency, not the average.
- Cost per unit: measure cost per thousand orders, not just total cost; good scaling keeps it flat or falling as volume grows.
Exam tip: For “vertical vs horizontal scaling”, give the definition, a comparison table (cost, limit, failure impact, complexity, examples) and one example each (a bigger database server; adding web servers behind a load balancer). For estimation questions, always write your assumptions first, then show each step with units.
Four ways to scale
Vertical (scale up)
Bigger CPU, more RAM, faster disks on one machine. No code changes. Hits a ceiling; single point of failure.
Bigger machineHorizontal (scale out)
More identical machines behind a load balancer. Near-linear growth for stateless work; needs distributed design for data.
More machinesDiagonal
Scale up each node to a sensible size, then scale out with many of them. What most real clusters do.
BothElastic (autoscaling)
The number of machines follows demand automatically: more at 8 pm on Diwali, fewer at 4 am.
Follows demandSide by side
| Scale up (vertical) | Scale out (horizontal) | |
|---|---|---|
| How | Bigger machine | More machines + load balancer |
| Limit | Largest machine available | Practically very high (coordination limits it) |
| Cost curve | Rises steeply at the top end | Roughly linear with machines |
| Failure | One machine down = outage | One of many down = reduced capacity |
| Code changes | Usually none | Stateless design, data partitioning |
| Easy for | Databases, legacy apps | Web/API servers, Spark, Kafka, Cassandra |
| Elasticity | Resize needs a restart | Add/remove nodes while running |
In interviews, say both: “I would first scale up the database to a sensible size and add read replicas; the stateless API tier scales out behind a load balancer; when writes outgrow one database, I would shard.” That shows you know when each approach fits.
A complete estimate for 10 crore orders a day
Write your assumptions
10 crore orders on Diwali day · peak hour is 5× the average · each order record ≈ 1 KB · one API server handles ≈ 400 orders/s (from a load test) · keep 3 copies of data.
Assumptions first, out loud. If one is wrong, the interviewer (or your manager) can correct it and the rest of the calculation still stands.Orders per second, average and peak
Dividing by about 1 lakh seconds turns “per day” into “per second”. Systems must handle the peak, not the average.orders_per_day = 10_00_00_000 # 10 crore avg_per_s = orders_per_day / 1_00_000 # ~1 lakh seconds in a day peak_per_s = avg_per_s * 5 # Diwali dinner peak print("average:", avg_per_s, "orders/s | peak:", peak_per_s, "orders/s")average: 1000.0 orders/s | peak: 5000.0 orders/sStorage per day and per year
About 95 GB a day is manageable; the yearly total with copies, around 100 TB, is what drives cost. Add indexes, logs and events and the real figure is several times larger.orders_per_day = 10_00_00_000 kb_per_order = 1 gb_per_day = orders_per_day * kb_per_order / 1024 / 1024 tb_per_year = gb_per_day * 365 / 1024 print(f"{gb_per_day:.0f} GB/day, {tb_per_year:.0f} TB/year, {tb_per_year * 3:.0f} TB with 3 copies")95 GB/day, 34 TB/year, 102 TB with 3 copiesServers at the peak
Divide the peak by what one server handles at the target load. The API tier is stateless, so these servers scale out behind a load balancer.peak_per_s = 5000 per_node = 400 # orders/s one API server can handle (measured) target_load = 0.6 # keep 40% headroom import math print("API servers at peak:", math.ceil(peak_per_s / (per_node * target_load)))API servers at peak: 21Network bandwidth
Order data alone is small for the network. Images, app events and replication traffic usually dominate bandwidth, so estimate them separately.peak_per_s, kb = 5000, 1 mb_per_s = peak_per_s * kb / 1024 print(f"incoming: {mb_per_s:.1f} MB/s = {mb_per_s * 8:.0f} Mbps")incoming: 4.9 MB/s = 39 MbpsConcurrency with Little’s law
At the peak about 1,000 requests are in progress at once. That sizes database connection pools and thread counts: too few and requests queue, too many and the database is overwhelmed.arrival = 5000 # requests per second time_inside = 0.2 # seconds each request spends in the system print("requests in flight:", int(arrival * time_inside))requests in flight: 1000
Diwali night: what Riya sees, and what the autoscaler does
Step through the six frames. Left: the app as Riya experiences it. Right: orders per second and the number of servers behind it.
- 6 pm: Diwali offers are live; Riya can order in about 30 seconds.
- 7:30 pm: the app is busier, but nothing feels different to her.
- 8 pm: very busy, still fast.
- 8:30 pm: the dinner peak; checkout still takes about a second.
- 9:30 pm: orders keep flowing smoothly.
- 11:30 pm: a quiet night; the festival rush is over.
- Normal evening load runs on 10 API servers at 45% CPU.
- Orders double; CPU crosses 70%, so the autoscaler starts adding servers (16 so far). New servers take a few minutes to boot, which is why teams pre-scale for known peaks.
- The autoscaler has added more servers: 32 now share the load, and CPU is back near the 60% target.
- At the peak, 40 stateless servers behind the load balancer handle the traffic. The database tier was scaled up and given read replicas the day before.
- Load falls and the autoscaler removes servers gradually (24), keeping a safety margin.
- Back to 10 servers. The company paid for 40 servers only for the hours it needed them. (Numbers are illustrative.)
Real-time scenarios: scaling in practice
Three live scenarios: a two-minute capacity estimate, a cart that vanishes when servers are added, and an advanced look at why more machines can mean less speed.
Real-time scenario 1Estimating Diwali day in two minutes
Watch Kabir answer the interview question line by line. Every step is simple arithmetic with round numbers; the skill is the order of the steps and writing down the assumptions.
- Write the given number: 10 crore orders on the day.
- Divide by about 1 lakh seconds: around 1,000 orders per second on average, and about 5,000 per second at the dinner peak.
- Multiply by the record size: at 1 KB per order, about 95 GB a day.
- Over a year, with three copies for safety, that is roughly 100 TB, which tells you storage is a cost to plan for.
- Servers: 5,000 per second ÷ (400 per server × 60% target load) ≈ 21 API servers at peak. Say the assumptions aloud so they can be challenged.
Why Riya’s cart vanished when we added servers
Scaling out is easy only for stateless services. Watch what happens to a cart stored in a server’s memory when a second and third server are added.
- With one server, keeping Riya’s cart in that server’s memory works perfectly.
- Two more servers are added. The load balancer sends her next tap to API 2, which has no idea what is in her cart. The cart appears empty.
- A quick fix is sticky sessions: always route Riya to API 1. But if API 1 restarts, the cart is lost, and some servers end up much busier than others.
- The real fix is to move the state out: carts live in Redis, so every API server is stateless and any server can handle any request.
- Now scaling out is simply adding identical copies, and servers can be replaced or removed at any time without losing anything.
When 64 machines are slower than 32
Adding machines is not free: they wait for shared resources and must talk to each other to stay in agreement. The Universal Scalability Law shows the effect. Watch throughput as a job grows from 8 to 64 nodes.
- With 8 nodes the job runs about 6.4 times faster than on one node. Some time is lost waiting for shared resources such as one database or one lock: contention.
- With 16 nodes, about 10 times faster. Each extra node adds less than the one before.
- Around 32 nodes the curve peaks near 12×. The nodes now spend a lot of time exchanging messages to stay consistent.
- With 64 nodes throughput falls to about 10×: slower than 32. The cost of every node coordinating with every other node grows faster than the work added: coherence.
- The fix is design, not hardware: partition data so nodes work independently, remove global locks, batch messages and avoid chatty synchronisation. (Curve uses α = 0.03, β = 0.0008 for illustration.)
Scaling decisions at real companies
1. Sale days and cricket finals: pre-scaling
Shopping and streaming apps scale out days in advance of known peaks (festive sales, IPL finals), run load tests at several times the expected traffic, and keep autoscaling as a safety net.
2. Databases: scale up, add replicas, then shard
A start-up’s single PostgreSQL server is first moved to a larger machine, then given read replicas for reports, and only when writes outgrow it is the data sharded or moved to a distributed database.
3. Spark clusters that grow per job
Data platforms start a cluster for a nightly job, let it autoscale from a few nodes to dozens while the heavy step runs, and shut it down afterwards, because storage lives separately in object storage.
4. System-design interviews
Most data and backend interviews include an estimation step: users, requests per second, storage per year, bandwidth and machine count, with stated assumptions. The method matters more than the exact number.
A back-of-envelope calculator
Change the assumptions at the top and run. The program turns “orders per day” into peak orders per second, storage per year and the number of servers. Then try the challenges: compare scaling up and out, simulate autoscaling, use Little's law, see the Universal Scalability Law and the tail-latency effect.
import math
orders_per_day = 10_00_00_000 # 10 crore
peak_factor = 5
kb_per_order = 1
copies = 3
per_server = 400 # orders/s one API server handles
target_load = 0.6 # keep 40% headroom
avg = orders_per_day / 86_400
peak = avg * peak_factor
gb_day = orders_per_day * kb_per_order / 1024 / 1024
tb_year = gb_day * 365 / 1024 * copies
servers = math.ceil(peak / (per_server * target_load))
print(f"average : {avg:,.0f} orders/s")
print(f"peak : {peak:,.0f} orders/s")
print(f"storage : {gb_day:,.0f} GB/day, {tb_year:,.0f} TB/year with {copies} copies")
print(f"servers : {servers} at {target_load:.0%} target load")avg = orders_per_day / 86_400Per day ÷ 86,400 seconds = per second. In your head, divide by 1 lakh.peak = avg * peak_factorSize for the busiest hour, never the average.servers = math.ceil(peak / (per_server * target_load))Round up, and plan for each server to run at only 60% so spikes and failures are absorbed.
Run it on your PC: an estimate calibrated by your own CPU
Good estimates start from a measurement. This program first measures how many order events one core of your computer can parse and validate in a second, then uses that number to estimate the cores, nodes and storage needed for 10 crore orders a day. Only the standard library is used. Save it as capacity_estimator.py and run it.
Measure, then estimate
"""Back-of-envelope capacity plan, calibrated with a real measurement on your PC.
Step 1 measures how many order events one CPU core can parse and validate per second.
Step 2 uses that number to estimate the machines needed for a festival peak."""
import json, math, time
# ---- assumptions (change these)
ORDERS_PER_DAY = 10_00_00_000 # 10 crore orders a day
PEAK_FACTOR = 5 # peak hour is 5x the average rate
EVENTS_PER_ORDER = 20 # clicks, status updates, pings per order
BYTES_PER_EVENT = 500
CORES_PER_NODE = 16
HEADROOM = 0.6 # run nodes at 60% so spikes and failures are absorbed
def inr(x):
"""Indian digit grouping: 115741 -> 1,15,741"""
s = str(round(x))
head, tail = s[:-3], s[-3:]
while len(head) > 2:
tail = head[-2:] + "," + tail
head = head[:-2]
return (head + "," + tail) if head else tail
# ---- step 1: measure one core
event = json.dumps({"order_id": "O1043", "user": "U9", "city": "Pune", "amount": 420,
"items": [{"dish": "thali", "qty": 1}, {"dish": "lassi", "qty": 2}], "ts": 1792411000})
n, start = 0, time.perf_counter()
while time.perf_counter() - start < 1.0:
e = json.loads(event)
ok = e["amount"] > 0 and e["city"] and len(e["items"]) > 0
n += 1
per_core = n / (time.perf_counter() - start)
print(f"Measured: one core parses and checks about {inr(per_core)} events per second")
# ---- step 2: estimate
events_day = ORDERS_PER_DAY * EVENTS_PER_ORDER
avg_eps = events_day / 86_400
peak_eps = avg_eps * PEAK_FACTOR
cores = peak_eps / (per_core * HEADROOM)
nodes = math.ceil(cores / CORES_PER_NODE)
storage_tb_day = events_day * BYTES_PER_EVENT / 1024**4
print(f"\nEvents per day : {events_day / 1e7:.0f} crore")
print(f"Average events / s : {inr(avg_eps)}")
print(f"Peak events / s : {inr(peak_eps)}")
print(f"Cores needed at peak: {cores:.1f} (at {HEADROOM:.0%} target load)")
print(f"Nodes ({CORES_PER_NODE} cores each): {nodes}")
print(f"Raw event storage : {storage_tb_day:.2f} TB per day, {storage_tb_day * 365:.0f} TB per year")
print("\nReal services do far more work per event (network, database, logging),")
print("so production capacity is usually 10-100x higher than this lower bound.")
# In a terminal: python capacity_estimator.pyRecorded from a real run with Python 3.13 on a cloud machine; your measured rate will differ. The surprising result, a single node, is the lesson: parsing JSON is cheap, so for this workload the real limits are storage (hundreds of TB a year), network, databases and reliability, not CPU. That is why you estimate every resource, not just one.
An autoscaler deciding, one line at a time
For three hours of traffic, the autoscaler picks enough servers to keep each one at about 60% load, never fewer than 2. Watch servers follow the load up and down.
Worked examples: real problems, solved and traced in memory
Four scaling and estimation problems, from a first estimate to choosing the best cluster size. Each runs live and is traced.
Requests per second from daily users
Problem statement
An app has 2 crore daily users. Each opens it 5 times a day and makes 8 API calls per visit. The evening peak is 4 times the average. Estimate average and peak requests per second.
2 crore users × 5 visits × 8 calls, peak 4×
requests/day: 80 crore average: 9,259/s peak: 37,037/s
Approach
- Requests per day = users × visits × calls.
- Average per second = per day ÷ 86,400.
- Peak = average × peak factor.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Storage for a year, with growth and copies
Problem statement
A service stores 120 GB of new data per day today, growing 5% per month. Data is kept 1 year with 3 copies. How much raw storage is needed at the end of the year?
120 GB/day, +5% per month, 12 months, 3 copies
data for the year: 56.0 TB with 3 copies : 167.9 TB daily volume at year end: 216 GB
Approach
- Loop month by month; daily volume grows 5% each month.
- Add 30 days of data per month to the running total.
- Multiply the year's total by the number of copies.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
How many servers each hour, and the saving
Problem statement
Each server handles 500 requests/s; keep load at 60% and at least 3 servers. Given hourly traffic, compute servers per hour and compare total server-hours with running the peak count all day.
6 hourly loads
servers per hour: [3, 8, 24, 30, 14, 3] autoscaled server-hours: 82 fixed at peak : 180
Approach
- Servers per hour = max(3, ceil(load ÷ (500 × 0.6))).
- Add up server-hours.
- Compare with peak servers × 6 hours.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Pick the best cluster size from a fitted model
Problem statement
Load tests were fitted to the Universal Scalability Law with a = 0.03 and b = 0.0008; one node does 10 jobs per minute. Each node costs Rs 50 per hour. Among the candidate sizes, find the fastest cluster and the cheapest cost per 1,000 jobs.
sizes 8, 16, 32, 48, 64 · a = 0.03 · b = 0.0008 · Rs 50/node-hour
8 nodes: 63.8 jobs/min, Rs 104.6 per 1,000 jobs 16 nodes: 97.4 jobs/min, Rs 136.8 per 1,000 jobs 32 nodes: 117.5 jobs/min, Rs 227.0 per 1,000 jobs 48 nodes: 113.9 jobs/min, Rs 351.2 per 1,000 jobs 64 nodes: 104.7 jobs/min, Rs 509.6 per 1,000 jobs fastest: 32 nodes | cheapest per job: 8 nodes
Approach
- Throughput(N) = 10 × N ÷ (1 + a(N−1) + bN(N−1)) jobs per minute.
- Cost per 1,000 jobs = (N × 50 per hour) ÷ (jobs per hour) × 1,000.
- Track the best size for speed and for cost separately.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Check yourself
Quick revision
Lesson 3.1 in one screen
- Scale up = bigger machine (simple, ceiling, single point of failure). Scale out = more machines + load balancer.
- Stateless tiers scale out by adding copies; move state to Redis/databases. Databases: scale up → read replicas → shard.
- Autoscale on CPU/queue/RPS with min and max; pre-scale for known peaks; keep 30–50% headroom.
- Estimation recipe: per day → ÷ 1 lakh → × peak factor → × size → ÷ per-machine capacity. State assumptions.
- Handy: 1 crore × 1 KB ≈ 10 GB; 1 Gbps ≈ 125 MB/s; 1 year ≈ 3 crore seconds.
- Little's law: L = λW. USL: contention (α) and coherence (β) limit scaling.
- Fan-out amplifies tail latency: 1 − (1 − p)N. Watch p99, not the average.
Interview and viva questions
Differentiate vertical and horizontal scaling.
Vertical scaling adds resources (CPU, RAM, disk) to a single machine; it is simple and needs no code changes, but cost rises steeply, there is a hard upper limit and the machine remains a single point of failure. Horizontal scaling adds more machines and spreads the load with a load balancer or data partitioning; it scales much further and tolerates failures, but requires stateless services or distributed data design. Real systems usually combine both.
What is a back-of-envelope estimation and why is it useful?
A quick calculation using rounded numbers and stated assumptions to estimate load (requests per second), storage, bandwidth and machine count. It shows early whether a design is feasible, which resource is likely to be the bottleneck and roughly what it will cost, before any detailed work. It is a standard part of system-design interviews.
How do you scale a database?
First optimise queries and indexes and scale the server up to a sensible size. Add caching for hot reads and read replicas for read-heavy workloads. When writes or data size outgrow one machine, partition (shard) the data across servers by a key, or move to a distributed database designed for horizontal scaling.
What is autoscaling?
Automatically adjusting the number of running instances based on metrics such as CPU utilisation, request rate or queue length, within configured minimum and maximum limits. It keeps performance steady during peaks and saves cost during quiet periods; for predictable peaks, teams schedule scale-ups in advance because new instances take time to start.
Why doesn't doubling the machines always halve the time?
Because part of the work can't be parallelised (Amdahl's law), machines wait on shared resources (contention), and they must communicate and coordinate (coherence), which grows with the number of machines. The Universal Scalability Law models these effects; beyond an optimal size, adding machines can even reduce throughput.
Practice problems
Estimation and scaling exercises. The last two are business tasks.
Per day to per second
Convert 4 crore events per day into events per second.
4 crore/day
463
Show solution and explanation
print(round(4_00_00_000 / 86_400))How it works: Divide by the 86,400 seconds in a day (≈ 1 lakh).
Peak rate
The average is 1,200 requests/s and the peak factor is 6. Print the peak.
1200, 6
7200
Show solution and explanation
print(1200 * 6)How it works: Systems are sized for the peak.
Daily storage
5 crore records a day at 2 KB each. Print GB per day.
5 crore × 2 KB
95 GB
Show solution and explanation
print(round(5_00_00_000 * 2 / 1024 / 1024), "GB")How it works: KB ÷ 1024 = MB, ÷ 1024 again = GB.
Servers with headroom
Peak is 9,000 requests/s; one server handles 500/s; target load 60%. Print servers needed.
9000, 500, 0.6
30
Show solution and explanation
import math
print(math.ceil(9000 / (500 * 0.6)))How it works: Divide by the usable capacity per server, then round up.
Bandwidth
At peak, 20,000 events/s of 1.5 KB each arrive. Print MB/s and Mbps.
20,000 × 1.5 KB
29.3 MB/s = 234 Mbps
Show solution and explanation
mb = 20000 * 1.5 / 1024
print(round(mb, 1), "MB/s =", round(mb * 8), "Mbps")How it works: Multiply MB/s by 8 to get megabits per second, the unit network links use.
Connection pool size
At peak, 3,000 queries/s each take 40 ms in the database. How many connections are busy on average? Add 50% safety.
3000/s, 40 ms
busy: 120.0 | pool with 50% safety: 180
Show solution and explanation
busy = 3000 * 0.040
print("busy:", busy, "| pool with 50% safety:", round(busy * 1.5))How it works: Little's law (L = λW) sizes connection pools and thread pools.
Slow pages from fan-out
A search page queries 40 shards in parallel; each shard is slow 0.5% of the time. What percentage of pages are slow?
40 shards, 0.5%
18.2 %
Show solution and explanation
print(round((1 - (1 - 0.005) ** 40) * 100, 1), "%")How it works: Even rare slowness becomes common with fan-out; techniques like hedged requests send a backup request after a short delay.
Autoscaling vs fixed fleet
Peak needs 40 servers for 4 hours a day; the other 20 hours need 10. A server costs Rs 30 per hour. Compare the daily cost of a fixed 40-server fleet with autoscaling.
40 × 4 h + 10 × 20 h
fixed: Rs 28800 | autoscaled: Rs 10800 | saving: 62 %
Show solution and explanation
fixed = 40 * 24 * 30
auto = (40 * 4 + 10 * 20) * 30
print("fixed: Rs", fixed, "| autoscaled: Rs", auto, "| saving:", round((1 - auto / fixed) * 100), "%")How it works: Elastic capacity is one of the biggest cost benefits of the cloud. (Prices are illustrative.)
Cost per thousand orders
Infrastructure costs Rs 18 lakh a month for 9 crore orders. After growth it costs Rs 30 lakh for 18 crore orders. Print the cost per 1,000 orders before and after.
18 lakh / 9 crore, 30 lakh / 18 crore
Rs 20.00 per 1,000 orders Rs 16.67 per 1,000 orders
Show solution and explanation
for cost, orders in [(18_00_000, 9_00_00_000), (30_00_000, 18_00_00_000)]:
print(f"Rs {cost / orders * 1000:.2f} per 1,000 orders")How it works: Good scaling lowers the unit cost as volume grows, even when the total bill rises.
Real-world scenario questions (asked in company interviews)
Scaling and estimation questions from interviews and production incidents. Decide your answer first, then open the model answer.
Estimate storage for a photo feature
A food app will let users attach up to 2 photos to each review. 50 lakh reviews are written a day and 20% include photos. Estimate storage per year.
The interviewer is checking: can you structure an estimate with clear assumptions and units?
Show a strong answer
Assume 1.5 photos per review that has photos, 3 MB per photo, plus 2 thumbnails of 100 KB. Photos per day = 50 lakh × 20% × 1.5 = 15 lakh. Storage per day ≈ 15 lakh × 3.2 MB ≈ 4.6 TB. Per year ≈ 1.7 PB before replication (object storage handles durability internally). Then discuss: compress or resize images, move old photos to cheaper tiers, serve through a CDN.
Avoid: jumping to a number without assumptions, or forgetting thumbnails and tiers.
The API is slow at 8 pm every day
A delivery app’s API slows down every evening. Adding more API servers didn’t help.
The interviewer is checking: do you find the real bottleneck before scaling?
Show a strong answer
- Check where time goes: API CPU, database CPU and connections, cache hit ratio, external calls.
- If the database is the bottleneck, more API servers just add more database connections and make it worse.
- Fixes: cache hot reads, add read replicas, tune slow queries and indexes, pool connections, scale the database up; shard if writes are the limit.
- Then autoscale the stateless tier with a sensible maximum.
Avoid: adding servers blindly; scaling the wrong tier.
Users get logged out randomly
After moving from one server to five behind a load balancer, users complain of random logouts and empty carts.
The interviewer is checking: do you recognise in-memory session state?
Show a strong answer
Sessions and carts are stored in each server’s memory, so requests landing on another server don’t find them. Move session state to a shared store (Redis) or use signed tokens (JWT) for authentication, and store carts in Redis or the database. Avoid relying on sticky sessions, which break when servers restart and unbalance load.
Avoid: increasing session timeouts or pinning all users to one server.
Doubling the cluster made the job slower
A distributed job ran in 40 minutes on 50 nodes. On 100 nodes it took 55 minutes.
The interviewer is checking: do you understand contention and coordination costs?
Show a strong answer
Look for shared bottlenecks (one metadata service, one database, a global lock, a single reducer) and coordination overhead (shuffles that grow with node count, many small tasks, frequent synchronisation). Measure stage by stage. Fixes include fewer, larger partitions, reducing shuffles (broadcast small tables, pre-partition data), removing global locks, and choosing the node count near the measured optimum rather than “as many as possible”.
Avoid: assuming linear scaling and adding even more nodes.