“Twenty percent higher” is not yet a capacity diagnosis.
The on-call dashboard compares 54,000 requests over the last minute with a previous hour’s chart. Another panel shows a steady 900 requests per second. A teammate calls the change “20%.” These observations may use different windows or denominators, so the percentage is not ready to guide a scaling decision.
Write down the count, the elapsed time, the service boundary being counted, and the capacity estimate. If the window changes, compare equivalent windows before inferring growth.
- Observed count
- 54,000 completed requests in 60 seconds.
- Estimated capacity
- 1,200 requests per second under this workload.
- Question
- What rate did the service see, and how much of that estimated capacity did it use?
- Invariant
- Compare the same request class and time window; do not turn a count into a rate without its duration.
A rate is a ratio with units that tell you what it means.
A ratio compares two quantities. A rate compares
quantities with different dimensions, often work over time: requests / seconds, bytes / second, or errors / requests. Units are part of the answer. 900 requests/minute and 900 requests/second differ by a factor of 60.
For throughput, divide the number of completed operations by elapsed time: 54,000 requests ÷ 60 seconds = 900 requests/second. Keep the count tied to the interval in which those completions were observed. A short
burst and a minute-wide average can have the same count only if their windows also match.
Proportion describes a part relative to a whole. Utilization is the
observed work rate divided by the capacity rate: 900 requests/s ÷ 1,200 requests/s = 0.75. The units cancel because both rates
measure the same kind of work per second; express 0.75 as 75%.
| Quantity | Calculation | Result | Use |
|---|---|---|---|
| Throughput | 54,000 requests ÷ 60 s | 900 requests/s | Observed completed work per unit time |
| Capacity rate | Given estimate | 1,200 requests/s | Maximum sustainable rate for this workload |
| Utilization | 900 ÷ 1,200 | 0.75 = 75% | Observed rate as a fraction of estimated capacity |
Percent change and percentage-point change answer different questions.
Suppose a cache hit rate moves from 90% to 95%. The change is 5 percentage points. Relative to the original 90%, it is a (95 − 90) ÷ 90 ≈ 5.56% increase. Both
calculations are valid, but they have different denominators. Name the one you mean.
Likewise, a 20% increase in demand does not mean utilization rises by 20 percentage
points. If a service starts at 75% utilization and demand rises by 20% while capacity
stays fixed, the new utilization is 75% × 1.20 = 90%—a 15 percentage-point
increase. That estimate assumes the same request mix and capacity.
Change one quantity and see which rate moves.
Change the measured count or window. Keep the capacity estimate for the same request type.
This calculator assumes the count and capacity cover the same request class and interval. It does not estimate tail latency or prove a safe operating limit.
Try doubling the count while holding the window and capacity fixed. The rate and utilization should double. Then double both count and window: the total work changes, but the rate should return to its starting value. That is why counts without durations can mislead.
Make invalid denominators visible.
The calculation is simple; the inputs determine whether it means anything. Reject a zero or negative duration and non-positive capacity. The returned utilization is a fraction; multiply by 100 only for a percent display. Keep validation and units explicit at the boundary.
Both versions preserve the rate units and reject invalid denominators.
export function requestsPerSecond(requests: number, elapsedSeconds: number): number {
if (!Number.isFinite(requests) || requests < 0 || !Number.isInteger(requests)) {
throw new Error('requests must be a non-negative integer');
}
if (!Number.isFinite(elapsedSeconds) || elapsedSeconds <= 0) {
throw new Error('elapsedSeconds must be positive');
}
return requests / elapsedSeconds;
}
export function utilization(ratePerSecond: number, capacityPerSecond: number): number {
if (!Number.isFinite(ratePerSecond) || ratePerSecond < 0)
throw new Error('rate must be non-negative');
if (!Number.isFinite(capacityPerSecond) || capacityPerSecond <= 0) {
throw new Error('capacity must be positive');
}
return ratePerSecond / capacityPerSecond;
}
package mathpractice
import (
"errors"
"math"
)
func RequestsPerSecond(requests, elapsedSeconds float64) (float64, error) {
if math.IsNaN(requests) || math.IsInf(requests, 0) || requests < 0 || math.Trunc(requests) != requests {
return 0, errors.New("requests must be a non-negative integer")
}
if math.IsNaN(elapsedSeconds) || math.IsInf(elapsedSeconds, 0) || elapsedSeconds <= 0 {
return 0, errors.New("elapsedSeconds must be positive")
}
return requests / elapsedSeconds, nil
}
func Utilization(ratePerSecond, capacityPerSecond float64) (float64, error) {
if math.IsNaN(ratePerSecond) || math.IsInf(ratePerSecond, 0) || ratePerSecond < 0 {
return 0, errors.New("rate must be non-negative")
}
if math.IsNaN(capacityPerSecond) || math.IsInf(capacityPerSecond, 0) || capacityPerSecond <= 0 {
return 0, errors.New("capacity must be positive")
}
return ratePerSecond / capacityPerSecond, nil
}
Use the ratio to ask a better operational question.
Keep this distinction: the arithmetic can be exact while the inputs are estimates. A
result such as 75% is only as trustworthy as the request count, duration, and capacity
measurement behind it.