Skip to content
About
Main content

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 . The metering rule, the increment and your utilization each move the 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

Is the "3× cheaper" engine, kept 20% busy, cheaper per useful unit than the pricier one kept 80% busy?

Run it

  1. 1Install uv once. It fetches Python and the lab's packages for you.

    Terminal
    curl -LsSf https://astral.sh/uv/install.sh | sh
  2. 2Get the script. It is the whole lab: read it, then download it.

    price-a-workload-from-the-metering.py44 linesDownload
    View 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)
    
  3. 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
Knobs
Knob · defaultWhat 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

The same workload, billed three ways
BillPer day
(a) quantity x rate7.5
(b) rounded up to the minimum90
(c) 12 jobs per slot7.5

Effective price = rate ÷ utilization

Effective price = rate ÷ utilization
EngineRateUtilizationEffective price
cheap rate, 20% utilized10.25
3x the rate, 80% utilized30.83.75
Reference runApple M2 · 8 cores · 8 GB
Output · uv run price-a-workload-from-the-metering.py
(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.75
macOS 26.6.2 · Python 3.13.3 · run 2026-10-20

What 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 , the 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 , 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.