Lesson 09 / 10
Pricing Models
Running on-demand, reserved, and spot capacity against the same load profile: total cost, work re-run because of reclaimed capacity, unused commitment, the gap between list price and effective unit price, and the liability left when exiting mid-window.
Published Updated
In this lesson
- Runs three capacity models with fictional prices against the same load profile and compares total cost
- Counts spot capacity's two hidden items: lost work and the cost of re-running it
- Measures unused commitment in reserved capacity and separates list price from effective unit price
- Shows with numbers across three profiles that the winning model changes as the load profile changes
Contents
The previous lesson counted cost as a pillar and found that none of the twelve decisions raised it. The reason was not that the decisions were poor, but that all of them were about what is rented. This lesson’s question is different: the same capacity, the same hour, the same work — under which terms is it rented.
There are three terms. On-demand capacity is taken the moment it is requested and stops being paid for the moment it is released; no decision is handed over, and the unit price is highest. Reserved capacity gives a discount in exchange for a commitment to pay for a fixed amount across the window; the decision handed over is the decision to shrink capacity. Spot capacity gives the largest discount, but the provider can reclaim it; the decision handed over is the decision of when the work gets cut. The measurement below runs these three terms under the same load.
Three Models and Load Profiles
- CG19. Prices are fictional and in fictional
unit/cu-hour: on-demand 1.00, reserved 0.62, spot 0.28. No real currency or price list is used. - CG20. The window is 168 hours. The unpredictable profile is generated with a self-written generator; the seed is visible as 20260804 and every run gives the same sequence.
- CG21. The reserved commitment R is the average demand, rounded, and is paid across the window whether it is used or not; demand exceeding R is served on-demand.
- CG22. Spot capacity’s reclaim probability depends on load: , at most 0.45.
- CG23. Only deferrable work runs on spot capacity. The deferrable share is 0.45 / 0.60 / 0.10 depending on the profile, because the three profiles are produced by different kinds of work.
- CG24. The work reclaimed capacity was doing is lost and re-runs on on-demand capacity; the re-run multiplier is 2.5 (the loss of intermediate state and re-queuing).
// price/model.mjs — pricing is a MODEL. Prices are FICTIONAL and in fictional // `unit/cu-hour`; no real currency, provider, or price list is used. export function generator(seed) { // self-written generator, seed visible let s = seed >>> 0; return () => ((s = (Math.imul(s, 1664525) + 1013904223) >>> 0) / 4294967296); } export const SEED = 20260804, HOURS = 168; // one-week window export const PRICE = { onDemand: 1.00, reserved: 0.62, spot: 0.28 }; // fictional unit/cu-hour export const RERUN = 2.5; // re-run multiplier for lost work export const MARKET_PEAK = 120; // fictional: scale point of reclaim probability export const reclaim = (d) => Math.min(0.45, 0.02 + 0.40 * (d / MARKET_PEAK)); // profile: demand series + deferrable share (differs because the profile's source work differs) export function profiles() { const r = generator(SEED); const flat = Array.from({ length: HOURS }, () => 40); const spiky = Array.from({ length: HOURS }, (_, t) => (t % 24 >= 2 && t % 24 <= 4 ? 120 : 20)); const unpredictable = Array.from({ length: HOURS }, () => (r() < 0.6 ? Math.round(r() * 6) : 55 + Math.round(r() * 85))); return { flat: [flat, 0.45], spiky: [spiky, 0.60], unpredictable: [unpredictable, 0.10] }; } export function run(model, demand, deferrable) { const R = Math.round(demand.reduce((a, b) => a + b, 0) / demand.length); // commitment = avg. demand let cost = 0, loss = 0, unusedCommitment = 0, rerunCost = 0; for (const d of demand) { if (model === "on-demand") cost += d * PRICE.onDemand; else if (model === "reserved") { cost += R * PRICE.reserved + Math.max(0, d - R) * PRICE.onDemand; unusedCommitment += Math.max(0, R - d); } else { const q = d * deferrable, p = reclaim(d), k = q * p; cost += q * (1 - p) * PRICE.spot + k * RERUN * PRICE.onDemand + (d - q) * PRICE.onDemand; loss += k; rerunCost += k * RERUN * PRICE.onDemand; } } return { model, cost, loss, unusedCommitment, rerunCost, R }; } export const MODEL = ["on-demand", "reserved", "spot"];
Three Runs Against the Same Profile
// price/run.mjs — three models run against the same load profile; total cost, work lost // on spot, and unused commitment on reserved are counted together import { profiles, run, MODEL, HOURS, SEED, PRICE } from "./model.mjs"; const s = (x, n) => String(x).padStart(n); const y = (x, n = 2) => x.toFixed(n); const P = profiles(); console.log(`seed ${SEED}, window ${HOURS} hours; prices are FICTIONAL unit/cu-hour: ` + `on-demand ${y(PRICE.onDemand)}, reserved ${y(PRICE.reserved)}, spot ${y(PRICE.spot)}.`); console.log(); console.log("profile | total demand | avg | lowest | highest | deferrable share | commitment R "); console.log("----------------|----------------|--------|----------|-----------|--------------------|---------------"); for (const [name, [t, e]] of Object.entries(P)) { const top = t.reduce((a, b) => a + b, 0); console.log(`${name.padEnd(15)} | ${s(top, 14)} | ${s(y(top / HOURS, 1), 6)} | ${s(Math.min(...t), 8)} | ` + `${s(Math.max(...t), 9)} | ${s(y(e), 18)} | ${s(run("reserved", t, e).R, 14)}`); } console.log(); console.log("profile | model | total cost | lost work | rerun cost | unused commitment "); console.log("----------------|--------------|----------------|---------------|---------------|--------------------"); const winner = {}; for (const [name, [t, e]] of Object.entries(P)) { let best = null; for (const m of MODEL) { const r = run(m, t, e); if (best === null || r.cost < best.cost) best = r; console.log(`${name.padEnd(15)} | ${m.padEnd(12)} | ${s(y(r.cost), 14)} | ` + `${s(r.loss ? y(r.loss, 1) : "-", 13)} | ${s(r.rerunCost ? y(r.rerunCost) : "-", 13)} | ` + `${s(r.unusedCommitment ? y(r.unusedCommitment, 1) : "-", 18)}`); } winner[name] = best; } console.log(); console.log("profile | winner | gap to 2nd | gap to 2nd % | gap to priciest % "); console.log("----------------|--------------|---------------|----------------|--------------------"); for (const [name, [t, e]] of Object.entries(P)) { const ranked = MODEL.map((m) => run(m, t, e)).sort((a, b) => a.cost - b.cost); const [a, b, c] = ranked; console.log(`${name.padEnd(15)} | ${a.model.padEnd(12)} | ${s(y(b.cost - a.cost), 13)} | ` + `${s(`${y((100 * (b.cost - a.cost)) / a.cost, 1)}%`, 14)} | ` + `${s(`${y((100 * (c.cost - a.cost)) / a.cost, 1)}%`, 18)}`); } console.log(`winning model across the three profiles: ${Object.values(winner).map((k) => k.model).join(", ")}.`);
seed 20260804, window 168 hours; prices are FICTIONAL unit/cu-hour: on-demand 1.00, reserved 0.62, spot 0.28. profile | total demand | avg | lowest | highest | deferrable share | commitment R ----------------|----------------|--------|----------|-----------|--------------------|--------------- flat | 6720 | 40.0 | 40 | 40 | 0.45 | 40 spiky | 5460 | 32.5 | 20 | 120 | 0.60 | 33 unpredictable | 7430 | 44.2 | 0 | 138 | 0.10 | 44 profile | model | total cost | lost work | rerun cost | unused commitment ----------------|--------------|----------------|---------------|---------------|-------------------- flat | on-demand | 6720.00 | - | - | - flat | reserved | 4166.40 | - | - | - flat | spot | 5572.09 | 463.7 | 1159.20 | - spiky | on-demand | 5460.00 | - | - | - spiky | reserved | 5264.28 | - | - | 1911.0 spiky | spot | 4850.46 | 787.9 | 1969.80 | - unpredictable | on-demand | 7430.00 | - | - | - unpredictable | reserved | 8528.04 | - | - | 3907.0 unpredictable | spot | 7475.12 | 261.3 | 653.25 | - profile | winner | gap to 2nd | gap to 2nd % | gap to priciest % ----------------|--------------|---------------|----------------|-------------------- flat | reserved | 1405.69 | 33.7% | 61.3% spiky | spot | 413.82 | 8.5% | 12.6% unpredictable | on-demand | 45.12 | 0.6% | 14.8% winning model across the three profiles: reserved, spot, on-demand.
The last line is this lesson’s finding: three different models win across the three profiles. In the flat profile, reserved capacity wins at 4,166.40 units, while the same load costs 6,720.00 on demand — 61.3 percent more; because the commitment is used in full, unused commitment is zero. In the spiky profile, reserved capacity loses, because the commitment is set to the average and 87.5 percent of demand falls below that average: 1,911 cu-hours are paid for but unused. In the unpredictable profile, no handoff pays off at all, and on-demand capacity, at the highest list price, wins at 7,430.00 units.
Spot capacity’s two hidden items sit in separate columns in the middle table. In the flat profile, 463.7 cu-hours of work is lost along with the reclaimed capacity, and re-running it costs 1,159.20 units — more than a fifth of that model’s total cost. In the spiky profile, the loss is 787.9 cu-hours and the re-run cost is 1,969.80 units; even though this profile is the winner, 40 percent of its cost is the price of reclaimed work. Spot capacity’s real price is not in the list; it is in the reclaim rate.
The spot row in the unpredictable profile shows this item’s second face: the lost work, at 261.3 cu-hours, is the lowest of the three profiles, yet the model still loses (7,475.12 against 7,430.00). The reason is not the loss but the ceiling on the gain — in that profile the deferrable share is 0.10, so the discount can only reach a tenth of demand. A discount is a discount only up to the share it can touch.
The third table gives the size of the gaps. In the flat profile, the winner beats the runner-up by 33.7 percent: model choice is a real decision here. In the unpredictable profile, the gap falls to 0.6 percent — the cost of the three models is nearly identical, and the choice is decided not by cost but by risk.
Effective Unit Price and the Cost of Exit
List price does not say how much a lease actually costs. What needs to be measured is the effective unit price: total cost divided by the total demand served.
// price/exit.mjs — effective unit price, the decision handed over, and the cost of exiting mid-window import { profiles, run, MODEL, HOURS, PRICE } from "./model.mjs"; const s = (x, n) => String(x).padStart(n); const y = (x, n = 3) => x.toFixed(n); const P = profiles(), EXIT_HOUR = 84; // the lease is exited mid-window const HANDOFF = { "on-demand": 0, reserved: 1, spot: 2 }; const LIST = { "on-demand": PRICE.onDemand, reserved: PRICE.reserved, spot: PRICE.spot }; console.log("profile | model | list | effective unit | vs. list | handoff "); console.log("----------------|--------------|--------|------------------|--------------|------------------"); for (const [name, [t, e]] of Object.entries(P)) { const top = t.reduce((a, b) => a + b, 0); for (const m of MODEL) { const effective = run(m, t, e).cost / top; console.log(`${name.padEnd(15)} | ${m.padEnd(12)} | ${s(y(LIST[m], 2), 6)} | ${s(y(effective), 16)} | ` + `${s(`x${y(effective / LIST[m], 2)}`, 12)} | ${s(HANDOFF[m], 17)}`); } } console.log(); console.log(`remaining liability when exiting in hour ${EXIT_HOUR} of the window (fictional units):`); console.log("profile | on-demand | spot | reserved (remaining commitment) | share of reserved gain "); console.log("----------------|--------------|-----------|-----------------------------------|--------------------------"); for (const [name, [t, e]] of Object.entries(P)) { const r = run("reserved", t, e), full = run("on-demand", t, e); const remaining = (HOURS - EXIT_HOUR) * r.R * PRICE.reserved; const gain = full.cost - r.cost; console.log(`${name.padEnd(15)} | ${s(0, 12)} | ${s(0, 9)} | ${s(y(remaining, 2), 33)} | ` + `${s(gain > 0 ? `${y((100 * remaining) / gain, 1)}%` : "no gain", 25)}`); }
profile | model | list | effective unit | vs. list | handoff ----------------|--------------|--------|------------------|--------------|------------------ flat | on-demand | 1.00 | 1.000 | x1.00 | 0 flat | reserved | 0.62 | 0.620 | x1.00 | 1 flat | spot | 0.28 | 0.829 | x2.96 | 2 spiky | on-demand | 1.00 | 1.000 | x1.00 | 0 spiky | reserved | 0.62 | 0.964 | x1.56 | 1 spiky | spot | 0.28 | 0.888 | x3.17 | 2 unpredictable | on-demand | 1.00 | 1.000 | x1.00 | 0 unpredictable | reserved | 0.62 | 1.148 | x1.85 | 1 unpredictable | spot | 0.28 | 1.006 | x3.59 | 2 remaining liability when exiting in hour 84 of the window (fictional units): profile | on-demand | spot | reserved (remaining commitment) | share of reserved gain ----------------|--------------|-----------|-----------------------------------|-------------------------- flat | 0 | 0 | 2083.20 | 81.6% spiky | 0 | 0 | 1718.64 | 878.1% unpredictable | 0 | 0 | 2291.52 | no gain
Spot capacity’s list price is 0.28, 72 percent below on-demand. Its effective unit price, though, is 0.829 in the flat profile, 0.888 in the spiky profile, and 1.006 in the unpredictable profile — 2.96, 3.17, and 3.59 times the list price, respectively. In the unpredictable profile, the model that looks cheapest actually ends up more expensive than the one that looks priciest. The same pattern plays out more mildly for reserved capacity: at a list of 0.62, the effective price is 0.964 in the spiky profile and 1.148 in the unpredictable one.
The handoff column ranks these three: 0 for on-demand, 1 for reserved, 2 for spot. The order matches the list discount exactly — the discount is the payoff for the decision handed over, and all three models’ effective price depends on how many times that handed-over decision worked against them.
The last table counts the exit. For on-demand and spot capacity, the remaining liability from exiting mid-window is zero. For reserved capacity, in the flat profile, 2,083.20 units remain — that is 81.6 percent of the total gain that same model produced in that profile. In the spiky profile, the remaining liability is 878.1 percent of the gain: exiting in the middle of a commitment wipes out the commitment’s entire gain eight times over. The exit cost of a lease, here, is not an estimate; it is a number the contract writes down directly.
Summary
- The three capacity models rank by the number of decisions handed over: on-demand 0, reserved 1 (shrinking capacity), spot 2 (when the work gets cut); the list discount follows this order.
- When the same three models run against three load profiles, three different models win: reserved in the flat profile, spot in the spiky profile, on-demand in the unpredictable profile. The runner-up costs 33.7, 8.5 and 0.6 percent more than the winner, respectively. There is no single correct model.
- Spot capacity’s hidden item is reclaimed work: 463.7 cu-hours lost and 1,159.20 units of re-run cost in the flat profile; 787.9 cu-hours and 1,969.80 units in the spiky profile.
- Reserved capacity’s hidden item is unused commitment: 1,911 cu-hours are paid for and unused in the spiky profile, 3,907 in the unpredictable profile.
- List price and effective unit price are separate numbers: spot’s list is 0.28, but its effective price ranges from 0.829 to 1.006 (2.96–3.59 times the list) and, in the unpredictable profile, exceeds on-demand.
- At exit, the remaining liability of on-demand and spot capacity is zero; for reserved capacity, exiting mid-window wipes out 81.6 percent of the gain in the flat profile and 878.1 percent in the spiky profile.
Next Step
Four lessons measured the geography and the price of rented infrastructure: failure-domain separation, proximity to the user, tension between pillars, and the terms under which capacity is taken. All four assumed one thing — that the work to be rented is already rented. But the fictional regional measurement network runs today in its own room, and it has to get here from somewhere. The same service can be moved as it stands, its individual pieces can be swapped for rented counterparts, or it can be rewritten from scratch; the three do not give the same result. The next lesson compares these three migration paths in terms of the decision handed over: how many decisions pass to the provider on each path, how many items get rewritten, how many waves the migration takes to complete, and, most importantly, how much the exit cost counted across these four lessons grows on each path.
To keep your progress and take notes, Log in
My notes
Log in to take notes.