Price a workload from the metering definition, not the rate
Simulate a modelNeeds Python
Problem
A price is never just a rate. Every bill has the form billed = ceil(q ÷ g) × g × r: the metered quantity q, rounded up to the billing increment g, times the rate r. A 5-second job on a service with a 60-second minimum is billed for 60 seconds, twelve times the work. And compute is billed for capacity over time, not for work done, so the price per useful hour is the rate divided by your utilization. The metering rule, the increment and your utilization each move the effective price more than the rate does.
Pick one real or invented workload (for example, 3 jobs of 5 seconds each, 1,000 times a day, on a service with a 60-second minimum). Write the bill three ways in a spreadsheet: (a) quantity × rate, (b) with the rounding rule billed = ceil(q ÷ g) × g × r, (c) after batching twelve jobs into each slot. Then repeat for an engine with a 3× lower rate and 20% utilization, and check that its effective price (rate ÷ utilization) is higher than a pricier engine at 80%.
The script does the spreadsheet for you; change the knobs to price your own workload.
The question. How much more than quantity × rate does the minimum increment cost for 5-second jobs, does batching win it back, and is the "3× cheaper" engine actually cheaper?
Predict
Expected result: (b) is about 12× (a) for 5-second jobs; (c) removes almost all of the difference.
Run it
1Install uv once. It fetches Python and the lab's packages for you.
Terminal curl -LsSf https://astral.sh/uv/install.sh | sh2Get the script. It is the whole lab: read it, then download it.
View the codeHide the code
price-a-workload-from-the-metering.py # /// script # requires-python = ">=3.10" # /// # Copyright (c) 2026 Aayush Sharma · SPDX-License-Identifier: MIT # https://thedataengineering.com/lab-code/LICENSE.txt """Price a workload from the metering. One workload billed three ways: quantity x rate, with a minimum billing increment (billed = ceil(q / g) x g x r), and after batching jobs into each billed slot. Then effective price = rate / utilization for a cheap, idle engine against a pricier, busy one. """ import argparse, json, math p = argparse.ArgumentParser(description=__doc__.splitlines()[0]) p.add_argument("--jobs", type=int, default=3, help="Jobs per run.") p.add_argument("--job-seconds", type=float, default=5, help="Seconds each job runs.") p.add_argument("--runs-per-day", type=int, default=1000, help="Runs per day.") p.add_argument("--minimum", type=float, default=60, help="Billing increment g, seconds (the minimum charge).") p.add_argument("--rate", type=float, default=0.0005, help="Price per second of compute.") p.add_argument("--batch", type=int, default=12, help="Jobs packed into one billed slot.") p.add_argument("--json", help="Also write the results to this JSON file.") args = p.parse_args() n = args.jobs * args.runs_per_day # jobs per day q = args.job_seconds a = n * q * args.rate # (a) quantity x rate b = n * math.ceil(q / args.minimum) * args.minimum * args.rate # (b) each job rounded up to g slots = math.ceil(n / args.batch) c = slots * math.ceil(args.batch * q / args.minimum) * args.minimum * args.rate # (c) batch jobs per slot bills = [{"method": "(a) quantity x rate", "per_day": a}, {"method": "(b) rounded up to the minimum", "per_day": b}, {"method": f"(c) {args.batch} jobs per slot", "per_day": c}] for r in bills: print(f"{r['method']:<32} {r['per_day']:8.2f} per day ({r['per_day'] / a:5.1f}x quantity x rate)") engines = [{"engine": "cheap rate, 20% utilized", "rate": 1.0, "utilization": 0.2}, {"engine": "3x the rate, 80% utilized", "rate": 3.0, "utilization": 0.8}] for e in engines: e["effective"] = e["rate"] / e["utilization"] print(f"{e['engine']:<28} rate {e['rate']:.1f} utilization {e['utilization']:.0%} effective price {e['effective']:.2f}") if args.json: with open(args.json, "w") as f: json.dump({"tables": {"bills": bills, "engines": engines, "summary": [{"rounding_x": b / a, "batched_x": c / a}]}}, f)3Run it in the folder you saved it to, with the defaults first. Then change a knob from the table below and run it again.
Terminal uv run price-a-workload-from-the-metering.py uv run price-a-workload-from-the-metering.py --help # every knob
| Knob · default | What it changes |
|---|---|
| --jobs3 | Jobs per run. |
| --job-seconds5 | Seconds each job runs. |
| --runs-per-day1000 | Runs per day. |
| --minimum60 | Billing increment g, seconds (the minimum charge). |
| --rate0.0005 | Price per second of compute. |
| --batch12 | Jobs packed into one billed slot. |
Our run
The same workload, billed three ways
| Bill | Per day |
|---|---|
| (a) quantity x rate | 7.5 |
| (b) rounded up to the minimum | 90 |
| (c) 12 jobs per slot | 7.5 |
Effective price = rate ÷ utilization
| Engine | Rate | Utilization | Effective price |
|---|---|---|---|
| cheap rate, 20% utilized | 1 | 0.2 | 5 |
| 3x the rate, 80% utilized | 3 | 0.8 | 3.75 |
(a) quantity x rate 7.50 per day ( 1.0x quantity x rate)
(b) rounded up to the minimum 90.00 per day ( 12.0x quantity x rate)
(c) 12 jobs per slot 7.50 per day ( 1.0x quantity x rate)
cheap rate, 20% utilized rate 1.0 utilization 20% effective price 5.00
3x the rate, 80% utilized rate 3.0 utilization 80% effective price 3.75What we found
Rounding each 5-second job up to the 60-second minimum made the bill 12× the quantity × rate, as expected, and packing twelve jobs into each slot brought it back to 1×. The "3× cheaper" engine at 20% utilization had an effective price of 5 per useful unit, against 3.75 for the pricier engine kept 80% busy.
Read it
Rates are the smallest term. The metering definition, the granularity and your utilization each move the effective price more than the rate does, and they multiply together. A forecast written as quantity × rate is wrong before it starts: write every line as metering definition, increment, minimum and expected utilization.
Batching is the general answer to a minimum increment: the fixed cost per operation sets a natural batch size, and here the fixed cost is the vendor's minimum charge. The same arithmetic applies to per-request fees, per-object storage minimums and per-query scan minimums.