By the end of this lesson
  • Convert daily active users (DAU) into average and peak read/write QPS
  • Calculate storage volume (SI vs. binary IEC units) and network bandwidth in bits/sec
  • Apply hardware latency hierarchy numbers across CPU caches, DDR5 DRAM, NVMe SSDs, and WAN links
  • Size in-memory cache budgets using active catalog working sets and the 80/20 Pareto rule

1. The problem

Suppose a product team announces a new photo-sharing feature for daily active users. Without running a single calculation, an architecture review quickly spirals into speculation: one engineer insists on sharding metadata across twelve Cassandra nodes on day one, while another assumes a single 10 Gbps origin server can stream every photo directly without a Content Delivery Network (CDN).

Both instincts fail in opposite directions:

  • Over-engineering metadata: As we will prove in five lines of arithmetic, five years of photo metadata fits comfortably under —well within a single primary PostgreSQL instance with read replicas. Prematurely sharding sacrifices ACID joins and adds months of operational complexity for zero gain.
  • Under-provisioning bandwidth: Streaming daily photo views drives average ( peak) of egress traffic, instantly saturating a single NIC if no CDN offloads edge reads.
SYSTEM ARCHITECTURE BLUEPRINTDISTRIBUTED SYSTEMS
Back-of-the-Envelope Estimation: Latency Hierarchy & Capacity Rules
ARCHITECTURAL FLOW · STEP 1 OF 3

1. L1 Cache Reference & Branch Mispredict & L2 Cache

Establishes the ingress and structural boundary across L1 Cache Reference & Branch Mispredict & L2 Cache: ~20 ns; ~100 ns.

Architectural Invariant: Maintains deterministic distributed systems invariants across L1 Cache Reference & Branch Mispredict & L2 Cache.
ARCHITECTURAL FLOW · STEP 2 OF 3

2. NVMe SSD Random Read & Intra-Datacenter Network RTT

Coordinates core data-plane execution and state transitions through NVMe SSD Random Read & Intra-Datacenter Network RTT: ~10-20 μs; ~500 μs; ~150 ms.

Architectural Invariant: Example: 86.4M reqs/day ÷ 86,400 = Exactly 1,000 avg QPS.
ARCHITECTURAL FLOW · STEP 3 OF 3

3. Powers of Two & Data Units & The 80/20 Pareto Caching Rule

Enforces durability, state convergence, and output verification across Powers of Two & Data Units & The 80/20 Pareto Caching Rule: 2^10 = 1,024 ≈ 1 Thousand (Kilobyte, KB) 2^20 = 1,048,576 ≈ 1 Million (Megabyte, MB) 2^30 = 1,073,741,824 ≈ 1 Billion (Gigabyte, GB) 2^40 ≈ 1 Trillion (Terabyte, TB) • 2^50 ≈ 1 Petabyte (PB); 80% of read traffic is generated by 20% of daily active content. Cache Budget = 20% × Daily Read Volume = 80%+ Cache Hit Ratio; Average QPS never occurs during prime evening hours. Always size database and network capacity for Peak = 3x Avg QPS.

Architectural Invariant: 80% of read traffic is generated by 20% of daily active content. Cache Budget = 20% × Daily Read Volume = 80%+ Cache Hit Ratio.

Back-of-the-envelope estimation (“napkin math”) is the discipline of computing rapid order-of-magnitude bounds ( precision) on QPS, storage, RAM working sets, and network bandwidth to validate physical feasibility before writing code.


2. Mental model

Treat capacity planning like municipal hydraulic engineering:

  • Total volume = Durable storage capacity ( on SSD or object storage).
  • Flow rate = Request throughput ( / ) and byte throughput ().
  • Pipe diameter = Network link capacity (, measured in bits per second, where ) and PCIe/memory bus bandwidth.
  • Pressurized local reservoir = In-memory caches () absorbing repetitive read bursts so the deeper well (NVMe disk or database engine) does not saturate.

Whenever incoming peak flow exceeds the narrowest pipe or worker pool along the request path, queues grow nonlinearly until timeouts and dropped connections cascade upstream.


3. Core concepts & unit discipline

Decimal (SI) vs. Binary (IEC) Prefixes

Production capacity errors frequently stem from mixing decimal SI prefixes (used by network NICs, S3 billing, and SSD manufacturers) with binary powers of two (used by OS memory allocators and GiB VM RAM specs):

PrefixDecimal (SI)Binary (IEC)DivergenceTypical Engineering Domain
Kilo / KibiPage size (), small JSON payload
Mega / MebiHigh-res photo, L2/L3 CPU cache
Giga / GibiNIC line rate (), Redis RAM ()
Tera / TebiEnterprise NVMe drive, relational DB volume
Peta / PebiObject-store data lake, CDN archive
TIP

Napkin Math Precision Rule: During initial estimation, treating is fine for order-of-magnitude discovery, provided you never confuse Bytes () with bits () ( error!) and you account for the gap when setting hard memory limits (maxmemory).

Seconds in a Day & Peak Traffic Multipliers

Real user traffic follows diurnal waves and event-driven spikes, so typically ranges from to for consumer apps (and for ticketing or flash-sale checkouts). For requests/day:

  • (mental shortcut: ).
  • .

4. Internal workings: The hardware latency hierarchy

Software architecture is governed by hardware physics. The table below reflects modern 2025–2026 server hardware (3–4 GHz Xeon/EPYC cores, DDR5 DRAM, PCIe 4.0/5.0 NVMe TLC SSDs) alongside a humanized time scale where :

OperationTypical Latency (Real)Humanized Scale ()
CPU L1 data cache hit
CPU L2 cache hit
Branch mispredict penalty
CPU L3 (LLC) cache hit
Uncontended user-space atomic / mutex
Main Memory (DDR5 DRAM) uncached read
NVMe SSD random read (, QD=1, TLC NAND)
SATA SSD random read ()
Intra-Availability-Zone network RTT
Rotational HDD seek + rotational latency
Transatlantic WAN RTT (New York London)
Transpacific WAN RTT (San Francisco Sydney)

Key architectural consequences:

  1. DRAM vs. Flash Gap: Reading DRAM () is to faster than an uncached NVMe flash read () and roughly faster than a rotational disk seek.
  2. Network Serialization Tax: Replacing 20 in-process function calls with 20 sequential cross-service RPCs inside a datacenter () burns the latency equivalent of DRAM accesses before executing any business logic.

5. Mathematics and worked example: Photo-sharing platform

Let us size a consumer photo-sharing platform end to end, stating every unit and assumption explicitly.

Workload Assumptions:

  • Daily Active Users (DAU): ().
  • Read Activity: Each active user views photos/day ( views/day), diurnal peak factor .
  • Write Activity: of DAU upload photo/day ( new photos/day), burst peak factor .
  • Payloads (Decimal SI): Average compressed photo blob (); relational metadata row (photo ID, user ID, creation timestamp, caption, object key).

Step 1: Throughput Estimation (QPS)

Conclusion: Read-to-write ratio is . Writes are tiny ( peak), whereas read delivery dominates capacity design.


Step 2: 5-Year Durable Storage Sizing

Binary Media (Object Storage):

Relational Metadata (Database):

Applying a B-tree index, tuple-header, and WAL overhead factor yields per database replica.

Conclusion: Media () belongs in erasure-coded object storage (e.g., Amazon S3) with lifecycle tiering after 90 days. Metadata ( physical footprint over 5 years) fits easily on a single unsharded PostgreSQL primary + standby/read replicas.


Step 3: Network Bandwidth (Average and Peak, in Bits/Sec)

Always size NICs and CDN contracts for peak flow and multiply bytes by :

Conclusion: Peak egress () exceeds commodity single-VM NIC allocations. Offloading of image reads to an edge CDN (e.g., Cloudflare CDN / CloudFront) drops origin egress below .


Step 4: Cache Memory Sizing (80/20 Pareto Working Set)

Suppose feed ranking draws primarily from the active 200-million-photo catalog (~6.6 months of recent uploads, or equivalently upper-bounding distinct photos accessed on a busy day by ). Under the 80/20 Pareto heuristic, caching the hottest of that active catalog serves of requests:

  • Why not cache media blobs in server DRAM? of DRAM—economically absurd when edge CDN SSD caches solve this cheaply.
  • Cache metadata rows in Redis: Accounting for Redis key-pointer, hash-bucket, and jemalloc overhead (), a single 32 GiB primary + replica Redis pair holds the entire hot metadata working set in memory.

6. Implementation: Capacity & Hardware Sizing Calculator in TypeScript

The calculator below distinguishes raw vs. physical storage overhead, converts peak byte rates into decimal , and computes binary Redis memory including allocator overhead.

export interface WorkloadProfile {
  dailyActiveUsers: number;
  writesPerUserPerDay: number;
  readsPerUserPerDay: number;
  writePayloadBytes: number;
  readMetadataBytes: number;
  activeCatalogItems: number;
  peakWriteMultiplier?: number;    // default 3.0
  peakReadMultiplier?: number;     // default 2.0
  replicationFactor?: number;      // default 3
  indexOverheadFactor?: number;    // default 1.5
  paretoWorkingSetRatio?: number;  // default 0.20
  cacheAllocatorOverhead?: number; // default 1.40 (Redis dict + jemalloc)
}

export interface CapacitySizingResult {
  writeQpsAvg: number;
  writeQpsPeak: number;
  readQpsAvg: number;
  readQpsPeak: number;
  dailyRawStorageTB: number;
  yearlyClusterStorageTB: number;
  peakIngressMbps: number;
  peakMetadataEgressMbps: number;
  recommendedCacheRamGiB: number;
}

export class CapacityEstimationCalculator {
  private static readonly SECONDS_PER_DAY = 86_400;
  private static readonly BYTES_PER_DECIMAL_TB = 1_000_000_000_000; // 10^12 B
  private static readonly BYTES_PER_BINARY_GIB = 1024 ** 3;         // 2^30 B

  public static calculate(profile: WorkloadProfile): CapacitySizingResult {
    const peakWrite = profile.peakWriteMultiplier ?? 3.0;
    const peakRead = profile.peakReadMultiplier ?? 2.0;
    const replication = profile.replicationFactor ?? 3;
    const indexOverhead = profile.indexOverheadFactor ?? 1.5;
    const pareto = profile.paretoWorkingSetRatio ?? 0.20;
    const cacheOverhead = profile.cacheAllocatorOverhead ?? 1.40;

    const totalWritesDay = profile.dailyActiveUsers * profile.writesPerUserPerDay;
    const totalReadsDay = profile.dailyActiveUsers * profile.readsPerUserPerDay;

    const writeQpsAvgExact = totalWritesDay / this.SECONDS_PER_DAY;
    const readQpsAvgExact = totalReadsDay / this.SECONDS_PER_DAY;

    const writeQpsAvg = Math.round(writeQpsAvgExact * 10) / 10;
    const writeQpsPeak = Math.round(writeQpsAvgExact * peakWrite);
    const readQpsAvg = Math.round(readQpsAvgExact);
    const readQpsPeak = Math.round(readQpsAvgExact * peakRead);

    const dailyRawBytes = totalWritesDay * profile.writePayloadBytes;
    const dailyRawStorageTB =
      Math.round((dailyRawBytes / this.BYTES_PER_DECIMAL_TB) * 100) / 100;

    const yearlyClusterBytes = dailyRawBytes * 365 * replication * indexOverhead;
    const yearlyClusterStorageTB =
      Math.round((yearlyClusterBytes / this.BYTES_PER_DECIMAL_TB) * 100) / 100;

    // Network bandwidth uses decimal SI bits/sec (1 Mbps = 10^6 bits/sec)
    const peakIngressMbps =
      Math.round(((writeQpsPeak * profile.writePayloadBytes * 8) / 1_000_000) * 10) / 10;
    const peakMetadataEgressMbps =
      Math.round(((readQpsPeak * profile.readMetadataBytes * 8) / 1_000_000) * 10) / 10;

    // Cache working set: 20% of distinct active catalog * payload * allocator factor, in binary GiB
    const hotPayloadBytes =
      profile.activeCatalogItems * pareto * profile.readMetadataBytes * cacheOverhead;
    const recommendedCacheRamGiB =
      Math.round((hotPayloadBytes / this.BYTES_PER_BINARY_GIB) * 10) / 10;

    return {
      writeQpsAvg,
      writeQpsPeak,
      readQpsAvg,
      readQpsPeak,
      dailyRawStorageTB,
      yearlyClusterStorageTB,
      peakIngressMbps,
      peakMetadataEgressMbps,
      recommendedCacheRamGiB,
    };
  }
}

export function runDemo(): void {
  console.log('=== Capacity Estimation: Photo-Sharing Platform Sizing ===');
  const profile: WorkloadProfile = {
    dailyActiveUsers: 10_000_000,
    writesPerUserPerDay: 0.1,        // 1M photo uploads/day
    readsPerUserPerDay: 20,          // 200M photo views/day
    writePayloadBytes: 2_000_000,    // 2 MB (decimal SI) compressed image
    readMetadataBytes: 500,          // 500 B metadata record
    activeCatalogItems: 200_000_000, // 200M active candidate photos
    peakWriteMultiplier: 3.0,
    peakReadMultiplier: 2.0,
    replicationFactor: 3,            // 3x object-store / replica durability
    indexOverheadFactor: 1.0,        // raw object blobs (no B-tree index on blobs)
    paretoWorkingSetRatio: 0.20,
    cacheAllocatorOverhead: 1.40,
  };

  const sizing = CapacityEstimationCalculator.calculate(profile);

  console.log(`[Throughput] Write QPS: ${sizing.writeQpsAvg} avg (${sizing.writeQpsPeak} peak)`);
  console.log(`[Throughput] Read QPS: ${sizing.readQpsAvg} avg (${sizing.readQpsPeak} peak)`);
  console.log(`[Storage] Daily Raw Media Upload: ${sizing.dailyRawStorageTB} TB`);
  console.log(`[Storage] 1-Year Replicated Media Footprint (3x): ${sizing.yearlyClusterStorageTB} TB`);
  console.log(
    `[Network] Peak Media Ingress: ${sizing.peakIngressMbps} Mbps | Peak Metadata API Egress: ${sizing.peakMetadataEgressMbps} Mbps`
  );
  console.log(
    `[Caching] Hot Metadata Redis Footprint (20% Pareto + 1.4x Allocator): ${sizing.recommendedCacheRamGiB} GiB`
  );
}

runDemo();

Simulation Execution Output & State Transitions

=== Capacity Estimation: Photo-Sharing Platform Sizing ===
[Throughput] Write QPS: 11.6 avg (35 peak)
[Throughput] Read QPS: 2315 avg (4630 peak)
[Storage] Daily Raw Media Upload: 2 TB
[Storage] 1-Year Replicated Media Footprint (3x): 2190 TB
[Network] Peak Media Ingress: 560 Mbps | Peak Metadata API Egress: 18.5 Mbps
[Caching] Hot Metadata Redis Footprint (20% Pareto + 1.4x Allocator): 26.1 GiB

7. Design alternatives & capacity trade-offs

Capacity estimates directly dictate which architectural alternative is physically and economically viable:

Bottleneck Exposed by MathOption A (Simpler / Lower Scale)Option B (Scaled / Specialized)Quantitative Crossover Threshold
Relational Storage & Write QPSSingle Primary PostgreSQL + Read ReplicasHorizontal Sharding / Distributed SQL (CockroachDB, Vitess, TiDB)Shard when sustained writes exceed or single-table working set exceeds
Read Latency & Hot-Key SkewDirect DB Index Lookups (OS Page Cache)Look-Aside In-Memory Cache (Redis / Memcached)Add external cache when read:write and DB CPU or IOPS approaches at peak
Large Binary Egress ()Stream directly from Origin App ServersPre-signed Object Store URLs (Amazon S3) + Edge CDNOffload to S3 + CDN as soon as peak media egress exceeds or global users exceed RTT
High-Volume Telemetry WritesRow-oriented B-Tree OLTP TableBatched Append-Only LSM / Columnar Store (ClickHouse, RocksDB)Switch when append rate exceeds or compression on time-series columns saves disk

8. Real-world implementation

  1. Discord (Gateway Connection Memory Sizing): By measuring per-WebSocket Erlang/Elixir process and buffer overhead ( per idle session), Discord capacity-plans gateway nodes for hundreds of thousands of concurrent connections while leaving headroom for burst fan-out serialization.
  2. X / Twitter (Home Timeline RAM Budgeting): Caching materialized home timelines stores compact 64-bit tweet/user identifiers ( including structure metadata per active user) in Redis clusters rather than full tweet JSON payloads, shrinking cluster RAM by an order of magnitude.
  3. Netflix Open Connect (Edge Bandwidth & Pareto Caching): Netflix embeds custom storage appliances ( NICs backed by NVMe flash) directly inside ISP peering facilities; overnight off-peak fills pre-populate the regional Pareto working set of titles so of evening peak video traffic never traverses backbone transit.

9. Failure modes and limitations

  1. The Average Fallacy: Provisioning for daily average QPS rather than peak QPS. Queueing delay explodes asymptotically as utilization during the peak hour, causing timeouts even when 24-hour average CPU looks like .
  2. Bytes-vs.-Bits () & SI-vs.-IEC () Confusion: Calculating of payload transfer and provisioning a NIC ( actual ceiling) saturates the network link at of expected load.
  3. Cloud Instance Network & EBS IOPS Ceilings: Sizing vCPUs and disk capacity while ignoring per-instance AWS EBS bandwidth caps or burstable NIC credit depletion.
  4. Ignoring Storage & Memory Amplification: Provisioning exact raw byte volume without multiplying by for B-tree indexes, WAL segments, LSM compaction headroom, or Redis key/allocator fragmentation.

10. Operational perspective

Estimation Validation Loop

[Napkin Estimate] ──► [Baseline Prototype] ──► [Open-Loop Load Test] ──► [Production Telemetry]
        ▲                                                                          │
        └─────────────────────── Calibrate Unit Costs ($/QPS, ms/IO) ──────────────┘
  • Unit-Cost Benchmarking: Treat napkin math as an initial hypothesis. Run an open-loop load generator (wrk2, k6) against a single node to measure empirical saturation QPS, bytes per record, and latency before multiplying out cluster size.
  • Utilization Headroom: Alert and autoscale at of measured CPU, NIC, or connection-pool saturation so traffic bursts and single-AZ failovers never push surviving nodes past the queueing knee.

11. Knowledge check

Review the interactive assessment quiz at the end of this lesson to verify your fluency with daily-to-QPS conversions, bytes-to-Gbps bandwidth math, DRAM-vs.-NVMe latency ratios, and Pareto cache sizing.


12. Exercises

Exercise 1: URL Shortener Capacity

  • Requirements: new URLs shortened per 30-day month. Read-to-write (redirect-to-create) ratio is , with a peak multiplier. Records are retained for 5 years at raw payload per mapping ( index/WAL overhead).
  • Tasks:
    1. Compute average write QPS, average read QPS, and peak read QPS (using ).
    2. Calculate 5-year raw database storage and physical storage per replica including index overhead.
    3. Calculate peak HTTP redirect egress bandwidth in assuming an HTTP 302 response header+body, and size a Redis cache for the hottest of the 5-year dataset (including allocator overhead).
View Reference Architecture & Worked Solutions (CORE-M01-001)

Task 1: Write & Read Throughput (QPS)

  • Seconds per 30-day month: .
  • Average Write QPS ():
  • Average Read (Redirect) QPS ( at ratio):
  • Peak Read QPS ( at peak multiplier):

Task 2: 5-Year Durable Database Storage Sizing

  • Total URLs created over 5 years ( months):
  • 5-Year Raw Storage Footprint ():
  • 5-Year Physical Storage per Database Replica ( B-tree index, tuple header & WAL overhead): (Architectural implication: While easily fits a single primary node, a B-tree working set benefits from range/hash table partitioning by creation epoch or short-code prefix so vacuuming and backups remain fast).

Task 3: Peak Redirect Network Bandwidth & Redis Cache Sizing

  • Peak HTTP 302 Egress Bandwidth ( response ): (Easily handled by commodity ingress load balancers).
  • Redis Cache Sizing (20% Pareto Working Set + Allocator Overhead):
    • Full 5-Year Catalog 20% Bound:
    • Practical Active-Window Refinement: Because URL clicks decay exponentially with link age (over of clicks target links created within the last 3 months URLs raw), caching the hottest of the active quarterly catalog requires only () of Redis DRAM while still sustaining hit rates.

13. Summary and decision guide

Estimation DimensionFormula / Rule of Thumb
Daily Volume QPS
Peak Provisioning (plus AZ failover headroom)
Network Bandwidth
Cache Working Set of distinct active catalog record size allocator factor
Physical DB DiskRaw payload for indexes, WAL, and compaction free space

14. Authoritative References & Version Context

MASTERY GATE · STEP 2 OF 94

Knowledge check & lesson completion

Score 4 / 4 (100%) on this check to mark this lesson Completed ✓, advance your position on the Master Spine, and add these concepts to your Spaced-Repetition Review Deck.

1. An ingestion pipeline processes 432 million telemetry events per day with a 4x diurnal peak factor. What are its approximate average QPS and provisioned peak QPS?
2. An image origin server experiences a peak egress rate of 2,500 responses/sec, with an average payload of 400 KB (decimal, 400,000 bytes) per response. What peak network bandwidth must be provisioned or offloaded to a CDN?
3. Reading a random 64-byte word from main memory (DDR5 DRAM) is approximately how much faster than a random 4 KiB uncached read from an enterprise PCIe 4.0 NVMe TLC SSD?
4. A service stores 50 million distinct active user profiles averaging 2 KB (2,000 bytes) of serialized payload. Using the 80/20 Pareto heuristic plus a 1.4x allocator/key-metadata overhead factor in Redis, approximately how much total Redis DRAM should be provisioned?
PERSONAL NOTEBOOK

Chapter Notes

Personal engineering insights and observations saved to this browser.

0 notes
Tip: You can also highlight text in the article above to quote directly.

Loading personal notes...