Scale-to-zero: a one-line break-even
Below what duty cycle does elastic capacity win, and why is cold start a billing problem?
Let d be the duty cycle: the fraction of time you are active. Let k be the premium the elastic rate carries over the provisioned rate. Going elastic wins when
d × k < 1, that is, when d < 1 ÷ k.
- the fraction of time you are active
- the premium the elastic rate carries over the provisioned rate
Data table
| Duty cycle d | series | Elastic ÷ provisioned |
|---|---|---|
| 0.01 | k = 2 | 0.02× |
| 0.022 | k = 2 | 0.045× |
| 0.035 | k = 2 | 0.07× |
| 0.047 | k = 2 | 0.094× |
| 0.06 | k = 2 | 0.119× |
| 0.072 | k = 2 | 0.144× |
| 0.084 | k = 2 | 0.168× |
| 0.097 | k = 2 | 0.193× |
| 0.109 | k = 2 | 0.218× |
| 0.121 | k = 2 | 0.243× |
| 0.134 | k = 2 | 0.268× |
| 0.146 | k = 2 | 0.292× |
| 0.159 | k = 2 | 0.317× |
| 0.171 | k = 2 | 0.342× |
| 0.183 | k = 2 | 0.366× |
| 0.196 | k = 2 | 0.391× |
| 0.208 | k = 2 | 0.416× |
| 0.22 | k = 2 | 0.441× |
| 0.233 | k = 2 | 0.466× |
| 0.245 | k = 2 | 0.49× |
| 0.258 | k = 2 | 0.515× |
| 0.27 | k = 2 | 0.54× |
| 0.282 | k = 2 | 0.565× |
| 0.295 | k = 2 | 0.589× |
| 0.307 | k = 2 | 0.614× |
| 0.319 | k = 2 | 0.639× |
| 0.332 | k = 2 | 0.663× |
| 0.344 | k = 2 | 0.688× |
| 0.356 | k = 2 | 0.713× |
| 0.369 | k = 2 | 0.738× |
| 0.381 | k = 2 | 0.762× |
| 0.394 | k = 2 | 0.787× |
| 0.406 | k = 2 | 0.812× |
| 0.418 | k = 2 | 0.837× |
| 0.431 | k = 2 | 0.861× |
| 0.443 | k = 2 | 0.886× |
| 0.456 | k = 2 | 0.911× |
| 0.468 | k = 2 | 0.936× |
| 0.48 | k = 2 | 0.96× |
| 0.493 | k = 2 | 0.985× |
| 0.505 | k = 2 | 1.01× |
| 0.517 | k = 2 | 1.03× |
| 0.53 | k = 2 | 1.06× |
| 0.542 | k = 2 | 1.08× |
| 0.554 | k = 2 | 1.11× |
| 0.567 | k = 2 | 1.13× |
| 0.579 | k = 2 | 1.16× |
| 0.592 | k = 2 | 1.18× |
| 0.604 | k = 2 | 1.21× |
| 0.616 | k = 2 | 1.23× |
| 0.629 | k = 2 | 1.26× |
| 0.641 | k = 2 | 1.28× |
| 0.653 | k = 2 | 1.31× |
| 0.666 | k = 2 | 1.33× |
| 0.678 | k = 2 | 1.36× |
| 0.691 | k = 2 | 1.38× |
| 0.703 | k = 2 | 1.41× |
| 0.715 | k = 2 | 1.43× |
| 0.728 | k = 2 | 1.46× |
| 0.74 | k = 2 | 1.48× |
| 0.752 | k = 2 | 1.5× |
| 0.765 | k = 2 | 1.53× |
| 0.777 | k = 2 | 1.55× |
| 0.79 | k = 2 | 1.58× |
| 0.802 | k = 2 | 1.6× |
| 0.814 | k = 2 | 1.63× |
| 0.827 | k = 2 | 1.65× |
| 0.839 | k = 2 | 1.68× |
| 0.851 | k = 2 | 1.7× |
| 0.864 | k = 2 | 1.73× |
| 0.876 | k = 2 | 1.75× |
| 0.889 | k = 2 | 1.78× |
| 0.901 | k = 2 | 1.8× |
| 0.913 | k = 2 | 1.83× |
| 0.926 | k = 2 | 1.85× |
| 0.938 | k = 2 | 1.88× |
| 0.95 | k = 2 | 1.9× |
| 0.963 | k = 2 | 1.93× |
| 0.975 | k = 2 | 1.95× |
| 0.988 | k = 2 | 1.98× |
| 1 | k = 2 | 2× |
| 0.01 | k = 3 | 0.03× |
| 0.022 | k = 3 | 0.067× |
| 0.035 | k = 3 | 0.104× |
| 0.047 | k = 3 | 0.141× |
| 0.06 | k = 3 | 0.179× |
| 0.072 | k = 3 | 0.216× |
| 0.084 | k = 3 | 0.253× |
| 0.097 | k = 3 | 0.29× |
| 0.109 | k = 3 | 0.327× |
| 0.121 | k = 3 | 0.364× |
| 0.134 | k = 3 | 0.401× |
| 0.146 | k = 3 | 0.438× |
| 0.159 | k = 3 | 0.476× |
| 0.171 | k = 3 | 0.513× |
| 0.183 | k = 3 | 0.55× |
| 0.196 | k = 3 | 0.587× |
| 0.208 | k = 3 | 0.624× |
| 0.22 | k = 3 | 0.661× |
| 0.233 | k = 3 | 0.698× |
| 0.245 | k = 3 | 0.735× |
| 0.258 | k = 3 | 0.772× |
| 0.27 | k = 3 | 0.81× |
| 0.282 | k = 3 | 0.847× |
| 0.295 | k = 3 | 0.884× |
| 0.307 | k = 3 | 0.921× |
| 0.319 | k = 3 | 0.958× |
| 0.332 | k = 3 | 0.995× |
| 0.344 | k = 3 | 1.03× |
| 0.356 | k = 3 | 1.07× |
| 0.369 | k = 3 | 1.11× |
| 0.381 | k = 3 | 1.14× |
| 0.394 | k = 3 | 1.18× |
| 0.406 | k = 3 | 1.22× |
| 0.418 | k = 3 | 1.26× |
| 0.431 | k = 3 | 1.29× |
| 0.443 | k = 3 | 1.33× |
| 0.456 | k = 3 | 1.37× |
| 0.468 | k = 3 | 1.4× |
| 0.48 | k = 3 | 1.44× |
| 0.493 | k = 3 | 1.48× |
| 0.505 | k = 3 | 1.52× |
| 0.517 | k = 3 | 1.55× |
| 0.53 | k = 3 | 1.59× |
| 0.542 | k = 3 | 1.63× |
| 0.554 | k = 3 | 1.66× |
| 0.567 | k = 3 | 1.7× |
| 0.579 | k = 3 | 1.74× |
| 0.592 | k = 3 | 1.77× |
| 0.604 | k = 3 | 1.81× |
| 0.616 | k = 3 | 1.85× |
| 0.629 | k = 3 | 1.89× |
| 0.641 | k = 3 | 1.92× |
| 0.653 | k = 3 | 1.96× |
| 0.666 | k = 3 | 2× |
| 0.678 | k = 3 | 2.03× |
| 0.691 | k = 3 | 2.07× |
| 0.703 | k = 3 | 2.11× |
| 0.715 | k = 3 | 2.15× |
| 0.728 | k = 3 | 2.18× |
| 0.74 | k = 3 | 2.22× |
| 0.752 | k = 3 | 2.26× |
| 0.765 | k = 3 | 2.29× |
| 0.777 | k = 3 | 2.33× |
| 0.79 | k = 3 | 2.37× |
| 0.802 | k = 3 | 2.41× |
| 0.814 | k = 3 | 2.44× |
| 0.827 | k = 3 | 2.48× |
| 0.839 | k = 3 | 2.52× |
| 0.851 | k = 3 | 2.55× |
| 0.864 | k = 3 | 2.59× |
| 0.876 | k = 3 | 2.63× |
| 0.889 | k = 3 | 2.67× |
| 0.901 | k = 3 | 2.7× |
| 0.913 | k = 3 | 2.74× |
| 0.926 | k = 3 | 2.78× |
| 0.938 | k = 3 | 2.81× |
| 0.95 | k = 3 | 2.85× |
| 0.963 | k = 3 | 2.89× |
| 0.975 | k = 3 | 2.93× |
| 0.988 | k = 3 | 2.96× |
| 1 | k = 3 | 3× |
That is the whole decision. It explains why the same architecture is obviously right for a development environment and obviously wrong for a platform with continuous traffic.
Cold start is a billing problem, not only a latency problem
Warm-up time is metered and produces no useful work. A 30-second start on a 5-second job is not a 30-second delay. The job is billed for (30 + 5) ÷ 5 = 7× the useful time, on top of any minimum increment.
Cold start is also not one quantity. It has five parts, with different sizes and different owners.
| Part | Typical owner |
|---|---|
| Provisioning the machine | vendor |
| Starting the runtime | vendor |
| Loading dependencies | you |
| JIT warm-up (the language runtime compiling hot code) | you |
| Cache warm-up (filling caches with the data you need) | you |
Only the first two are the vendor's problem. The last one is the dangerous one. A system whose steady-state speed depends on warm caches has a cold-start cost measured in minutes of degraded operation. This is hysteresis (the state of the past affects the present): the system does not return to normal speed the moment the machine is back. When it fails badly, it looks like a "cold-cache collapse": the backend sees all the traffic the cache used to absorb.
A correction to the standard claim
"You pay nothing when idle" is false wherever state exists. Storage keeps billing, and reactivation keeps billing. The genuinely worst case is neither continuous traffic nor zero traffic. It is sparse traffic: one request every few minutes. Then the system is always cold and never idle. If the idle timeout is shorter than the gap between requests, you pay a warm-up on nearly every request. If it is longer, the system never suspends and you pay for always-on capacity. Either way, scale-to-zero gave you nothing. This is exactly Tomás's development warehouse.