Skip to content

Performance Engineering and System Design Handbook

Appendix I — Capacity-Planning Workbook Specification

Build an auditable capacity workbook across workload mix, failure, recovery, storage, uncertainty, lead time, and cost.

Mercury’s annual plan is green. Forecast peak CPU stays below 60%, and the budget has room for another node. During review, a storage engineer asks one unfashionable question: after losing a storage member, how much bandwidth remains for repair while checkout traffic continues? The workbook has no repair row. The answer, reconstructed outside it, misses the recovery objective by nine hours.

The CPU forecast was arithmetically correct and operationally unsafe. A capacity workbook is not a bigger utilization spreadsheet. It is a dependency graph from operation-class demand → resource service demand → scenario-specific useful load → safe supply → time and cost decision. Every important failure, recovery, storage, quota, or lead-time constraint must be able to turn the plan red.

The machine-readable example at examples/performance-engineering-system-design-handbook/appendices/capacity-workbook/mercury-capacity-workbook.json and its dependency-free Node verifier demonstrate the schema and recompute the results. They are a review fixture, not a forecast oracle. Replace every demand, coefficient, price, uncertainty band, and failure assumption with scoped local evidence.

Workbook dependency map

Create separate worksheets or tables with stable IDs and explicit units. Do not hide dependencies inside presentation formulas.

Order Worksheet Consumes Produces
0 metadata and decisions ownership, boundary, source ledger versioned planning contract
1 operation demand historical demand, events, growth rate/volume by operation class and time shape
2 workload mix operation demand, tenant/key/payload distributions scenario mix and skew
3 service demand and coefficients experiments, telemetry, topology resource per useful operation
4 supply and constraints inventory, quotas, safe test results safe resource capacity by failure domain
5 scenarios sheets 1–4 nominal, peak, failure, and recovery utilization/headroom
6 storage and repair write/delete/retention, encoding, bandwidth growth date, rebuild time, recovery contention
7 forecast, uncertainty, and lead time demand forecast, scenario limits, procurement trigger and order-by dates
8 unit cost and budget scenario supply, quoted prices, labor/risk scope cost per valuable outcome and budget decision
9 risks and decisions all sheets owner, action, evidence gap, revisit trigger

One row should be traceable across sheets by operation, resource, scenario, failure domain, and time period. Keep raw observations or query references outside calculated cells. Record workbook revision, as-of date, environment, topology, software/configuration versions, evidence type, owner, reviewer, and decision supported.

Sheet 1: demand by operation class

Aggregate requests/s is rarely a stable capacity unit. Split operations when they differ materially in resource demand, critical path, payload, consistency, fan-out, priority, or growth. A useful schema is:

Field Meaning
operation_id stable join key such as browse, checkout, index_update
useful_unit correct completed operation, record, byte, token, query, or stream-minute
population tenant, geography, priority, API/version, or data class
time_bucket interval and timezone; retain peak shape rather than only monthly total
offered, admitted, valid_goodput separated rates or volumes with units
payload_distribution representative size/count quantiles or histogram reference
skew tenant/key/partition/locality distribution and concentration
growth_driver organic trend, launch, season, migration, replay, or policy change
evidence observed, estimated, modeled, simulated; query/source and uncertainty

Model attempts separately from originals. If a checkout has one original request and 1.2 downstream attempts on average, do not call both “1.2 requests” without retaining the amplification coefficient. Preserve scheduled bursts and concurrency where arrival rate alone cannot represent sessions or long-lived work.

For each planning period t, operation-class demand λ_i(t) should include a central estimate and an uncertainty range. Keep known events as explicit step functions rather than forcing them into a smooth trend.

Sheet 2: workload mix and skew

The mix sheet turns class demand into scenario-specific proportions and distributions. Include operation share, payload bands, cache-hit state, read/write ratio, tenant and hot-key concentration, geography, locality, protocol, background work, and optional versus mandatory paths.

Averages can invent a workload that no user sends. If 10% of checkouts carry large baskets whose service demand is four times the median, model that class or distribution directly. If one tenant produces 20% of writes to one partition, fleet capacity does not prove partition capacity.

The sheet must state how mix changes in peak, failure, and recovery states. Failure can redirect geography, reduce cache locality, change retry rate, or move a cheap read into an expensive reconstruction. Recovery adds replay, repair, rebalance, cache refill, compilation, or reconciliation. Treat those as demand, not footnotes.

Sheet 3: service demand and resource coefficients

For operation class i and resource r, define service demand D_i,r as resource consumed per useful operation, with a unit such as core-ms/op, MiB read/op, MiB transferred/op, IOPS/op, allocation bytes/op, connection-seconds/op, or accelerator-ms/op.

Required columns include:

  • operation, resource, topology/failure domain, and software/config version;
  • coefficient, unit, central estimate, dispersion or range;
  • workload and state under which it was measured;
  • observed useful-goodput denominator and correctness rule;
  • source experiment or telemetry query, date, sample coverage, and owner; and
  • scaling or validity range, nonlinear breakpoints, and transfer limit.

For offered rate λ_i, the first resource-demand model is

R_r = Σ_i (λ_i × D_i,r) + R_background,r

Dimensional analysis is mandatory: operations/s × core-ms/operation = core-ms/s. Do not mix decimal MB and binary MiB, logical and physical bytes, provisioned and achieved IOPS, or CPU utilization percentages from different core counts.

Coefficients are state-dependent. Cold cache, compaction, skew, throttling, encryption, retries, and larger payloads can change them. Where service demand rises with load, use ranges or piecewise coefficients and name the boundary. Never infer linear scaling beyond the last controlled observation without marking it modeled.

Sheet 4: supply and reachable constraints

Inventory physical and logical capacity by failure domain. Include compute, memory, storage space and performance, network, connections, file descriptors, partitions, metadata rates, quotas, licenses, power, human operations, and external dependency agreements.

For each resource record installed supply, currently available supply, tested maximum, declared safe limit, warm-up or provisioning delay, smallest scale increment, zone/region/cell placement, quota ceiling, and evidence. The safe limit is a decision, not a universal 70% target. CPU may safely run high for a batch system; a latency-sensitive queue may require earlier admission. A connection or partition ceiling can invalidate abundant CPU.

Separate theoretical, benchmarked, and production-observed capacity. Define safe capacity as the useful-goodput envelope that preserves objectives and correctness under the named scenario. Installed nodes multiplied by vendor specifications is not that envelope.

Sheet 5: nominal, peak, failure, and recovery scenarios

Every scenario names demand multiplier or explicit class rates, workload mix, available failure domains, optional work, background/recovery demand, objective, and duration. At minimum include:

  • nominal: sustained representative demand and maintenance;
  • peak: forecast event, burst shape, skew, and peak mix;
  • failure: N-minus-one or the most important domain/dependency loss with redirected demand;
  • recovery: new arrivals plus backlog drain, repair, replay, rebalance, and cold-state work.

For resource r in scenario s, calculate demand R_r,s, physical utilization against available supply, and headroom against the declared safe capacity C_safe,r,s:

H_r,s = 1 - (R_r,s / C_safe,r,s)

Negative headroom is a failed scenario, even if fleet-average utilization looks modest. Also expose step constraints: one more partition, subnet, license block, shard split, storage tier, or architectural redesign may have a different lead time than one node.

For each failed scenario, the workbook must name an action: reduce optional demand, change placement, reserve capacity, raise a quota, validate a new safe limit, or redesign. “Autoscaling” is incomplete unless detection, provisioning, initialization, state movement, quotas, cost, and failure availability fit inside the demand horizon.

Sheet 6: storage growth and repair bandwidth

Track logical ingest, updates, deletes, retention, compaction, indexes, replicas or erasure overhead, snapshots, tombstones, and temporary migration space. Calculate physical growth from lifecycle behavior, not only application payload.

A simple projected usable volume is

V(t) = V_0 + Σ_k ((ingest_k - delete_k) × Δt)

but real systems may require piecewise retention and compaction models. Record both usable and raw capacity and the safety reserve required for repair, rebalancing, or migration.

Repair time for bytes B_repair and effective net repair bandwidth W_repair begins as

T_repair = B_repair / W_repair
W_repair = W_reserved - W_foreground - W_other_recovery

This is valid only when units match and source read, destination write, network, checksums, reconstruction compute, metadata, throttles, and failure-domain limits can sustain the rate. The minimum of those paths is the real repair bandwidth. Test repair with foreground traffic and preserve a stop rule if it harms valuable goodput.

Sheet 7: uncertainty, lead time, and triggers

A single forecast line conceals decision risk. Keep central, low, and high cases or a documented interval, with trend, seasonality, launch steps, known migrations, and forecast error. Do not assign a confidence percentage without a defensible method and historical calibration.

For every constrained resource, calculate the earliest date a scenario crosses safe capacity under the high case. Work backward through:

  • measurement and decision cadence;
  • approval and budget time;
  • quota, procurement, construction, or reservation lead time;
  • deployment, data movement, warm-up, and validation;
  • rollback margin and forecast-error buffer.

The trigger is not “CPU reaches 80%.” It is a date or condition that leaves enough time for the chosen response. Replan when demand, mix, coefficient, topology, price, objective, failure assumption, forecast error, or lead time crosses its declared tolerance. Maintain a risk row for evidence gaps such as an unmeasured cold-cache coefficient or untested region-loss envelope.

Sheet 8: unit cost and budget

Cost the scenario that meets the objective, not the cheapest nominal state. Record currency, price date, region, commitment, tax/discount treatment, allocation rule, and owner. Include compute, storage, network/egress, managed services, licenses, observability, backup, recovery reserve, and material operational labor.

Choose a valuable denominator: cost per valid SLO-compliant checkout, GiB durably retained, query meeting freshness, or model output meeting quality—not cost per attempt. Separate fixed, variable, and step costs. Show marginal cost of the next unit and the cost of safety headroom. If budget caps supply, model the demand control or objective trade explicitly rather than forcing the capacity row green.

Mercury workbook walkthrough

The fixture uses three operation classes. At the base forecast, browse contributes 3,000 × 0.9 = 2,700 core-ms/s, checkout contributes 1,000 × 2.4 = 2,400 core-ms/s, and optional enrichment contributes 500 × 4 = 2,000 core-ms/s. Total modeled CPU demand is 7,100 core-ms/s.

Five four-core nodes provide 20,000 physical core-ms/s. Mercury declares 70% safe CPU supply for nominal and peak states based on its bounded environment, not as a universal rule. The verifier computes:

Scenario Modeled CPU demand Physical utilization Headroom against safe supply Decision
nominal, five nodes 7,100 core-ms/s 35.5% 49.3% inside envelope
1.3× peak, five nodes 9,230 core-ms/s 46.2% 34.1% inside envelope
zone loss, three nodes, enrichment shed 6,630 core-ms/s 55.3% 21.1% inside failure envelope
recovery plus 700 core-ms/s, three nodes 7,330 core-ms/s 61.1% 32.1% against a separately validated 90% recovery limit inside modeled recovery envelope

This does not prove the plan. The service-demand measurements, optional-path independence, node placement, safe limits, and recovery coefficient still require evidence. The table’s value is traceability: changing any input recomputes the scenario.

A 540,000-operation backlog with 1,300 new operations/s and 1,550 useful recovery completions/s has a first drain estimate of 36 minutes. The workbook rejects X_r ≤ λ and a drain beyond the 40-minute objective.

Storage begins at 40 TiB usable and grows 1.5 TiB/month to 58 TiB after twelve months, below a declared 64 TiB safe limit. Rebuilding 8 TiB with 400 MiB/s reserved and 110 MiB/s consumed by foreground traffic leaves 290 MiB/s and an arithmetic lower-bound of about 8.0 hours, inside the 12-hour objective. The reviewer must still test source, destination, network, and reconstruction limits.

The high-case safe-envelope crossing is 70 days away. With 45 days of resource lead time and a 14-day replanning margin, action is due in 11 days. Monthly compute, raw storage, network/services, and 15% contingency total $14,950 against a $16,000 scoped budget. Prices are quoted fixture inputs dated by the workbook, not public price claims.

Applied exercises

Build a twelve-month failure plan. Split a service into at least three operation classes. Add payload and tenant skew, measure or label service-demand coefficients, and calculate nominal, peak, largest-zone loss, and recovery states for CPU, memory, storage, network, connections, and one non-obvious quota. Identify the first constraint and work backward from its high-case crossing through lead time.

Expose a false linear model. A plan assumes doubling nodes doubles capacity. Add one shared database connection limit, hot partition, license ceiling, metadata rate, or repair path. Show where the step constraint invalidates node multiplication and state the experiment or redesign needed before the forecast can extend.

Trade cost without deleting resilience. Reduce Mercury’s monthly budget by 15%. Compare commitments, optional-demand control, retention, workload shaping, and objective changes. Do not remove failure or recovery headroom silently. Recalculate cost per valid checkout and name the residual risk owner.

Review card

  • Every operation class has a useful-work unit, time shape, mix/skew, evidence, and uncertainty.
  • Every consequential resource has a scoped service-demand coefficient and transfer limit.
  • Supply distinguishes physical, reachable, tested, and safe capacity by failure domain.
  • Nominal, peak, failure, and recovery scenarios include optional and background work.
  • Headroom is computed against safe scenario capacity, not theoretical fleet maximum.
  • Storage includes lifecycle amplification, temporary space, repair bandwidth, and foreground competition.
  • Forecast ranges connect safe-envelope crossings to procurement, warm-up, validation, and rollback lead time.
  • Cost uses valuable correct outcomes and includes reliability, recovery, and material operating work.
  • Risks, evidence gaps, owners, actions, and recalculation triggers are visible.
  • Formulas preserve units, stable IDs, source cells, and reviewable version history.

The decision rule is: commit capacity only from operation-class demand and scoped service demand tested across nominal, peak, failure, and recovery scenarios; act when the high-case safe-envelope crossing approaches the full delivery-and-validation lead time, and reject any plan whose storage, repair, quota, correctness, or budget constraint cannot independently turn the decision red.

Primary references and transfer notes

  • Microsoft Azure Well-Architected Framework, capacity planning describes forward-looking demand forecasts, scenario planning, resource requirements, limits, and the cost trade between over- and underprovisioning. Its product examples do not supply local coefficients or thresholds.
  • AWS Well-Architected Reliability Pillar connects quota headroom, demand adaptation, load testing, failure isolation, recovery testing, and disaster-recovery objectives. Treat its best-practice categories as review prompts, not proof of a workload’s envelope.
  • Google SRE, Addressing Cascading Failures explains why queues, retries, failover, overload, and recovery behavior must shape capacity beyond steady-state resource utilization.
  • Google SRE, Production Services Best Practices recommends establishing resource-to-capacity ratios through load testing and retaining headroom. A historical ratio must be revalidated as workload and implementation change.

The workbook makes constraints traceable in rows; reviewers still need to see where those constraints live and which paths compete. Appendix J supplies the shared diagram grammar for containment, state authority, waiting, objectives, and failure or recovery boundaries.