You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix: Usage events are emitted in units the meters do not declare #272
Compute usage events are emitted in units the meters do not declare, so any consumer that trusts the published unit reads a number a thousand times too large.
The billing contract asks services to emit an integer in the metric's base unit and let the meter's pricing unit carry the scale. The emitter instead scales the value itself, which the catalog has no way to know about.
Production prices none of these metrics today, so no invoice is affected. Staging does price them, and that is where the overstatement lands.
What success looks like
A consumer reading a compute usage event can multiply by the published rate and get the right cost
Fractional usage survives integer encoding without a scale factor the meter does not declare
Memory usage is emitted in the unit its meter declares
The mismatch is caught before the rates go live in production
The meters it feeds, from config/components/service-catalog/service-configuration.yaml:
Metric
Declared unit
Emitted
compute.datumapis.com/instance/cpu-seconds
s
vcpu-seconds x 1000
compute.datumapis.com/instance/memory-seconds
By.s
GiB-seconds x 1000
The memory mismatch is older than this change. memory_gib_seconds = (memory_bytes / 1073741824.0) * duration_s was already GiB-seconds against a byte-seconds meter, and the milli scaling multiplies an existing disagreement rather than creating it.
The contract in milo-os/billing docs/emitting-usage.md is direct about both:
Do not use floating point.Quantity is int64. Measure cpu-seconds, not cpu-hours; bytes, not gibibytes. The meter's consumedUnit/pricingUnit handle scale.
The problem behind the change is real. data.value is int64 on the wire, so a sub-second window stringified as "0.5" decodes to 0 and the usage disappears. Emitting byte-seconds rather than GiB-seconds solves that for memory without a scale factor, since byte-seconds are large integers by construction. CPU needs its own answer, either a longer aggregation window or a meter that declares the scale.
Rates, from the staging charges component: vcpu-second at 0.000014 and gib-second at 0.0000045 USD. Rate times a milli-unit value overstates by 1000.
Blast radius today is staging only. That charges component is included by staging overlays alone, so production emits these metrics with no rates attached and no invoice moves. Worth settling before production gets rates.
One immutability constraint to plan around: spec.metrics[].name, kind and unit are immutable once the ServiceConfiguration is Published, so a unit correction ships as a new metric name rather than an edit.
Activity
ecv commentedon Sep 3, 2026
cc @JoseSzycho, who has been working in this area.
Evidence, measured 2026-09-03.
The emitter is the
billing-usage-collector-unikraftcomponent in datum-cloud/infra, changed by 0e317bcc:The meters it feeds, from
config/components/service-catalog/service-configuration.yaml:compute.datumapis.com/instance/cpu-secondsscompute.datumapis.com/instance/memory-secondsBy.sThe memory mismatch is older than this change.
memory_gib_seconds = (memory_bytes / 1073741824.0) * duration_swas already GiB-seconds against a byte-seconds meter, and the milli scaling multiplies an existing disagreement rather than creating it.The contract in milo-os/billing
docs/emitting-usage.mdis direct about both:The problem behind the change is real.
data.valueis int64 on the wire, so a sub-second window stringified as"0.5"decodes to 0 and the usage disappears. Emitting byte-seconds rather than GiB-seconds solves that for memory without a scale factor, since byte-seconds are large integers by construction. CPU needs its own answer, either a longer aggregation window or a meter that declares the scale.Rates, from the staging charges component:
vcpu-secondat0.000014andgib-secondat0.0000045USD. Rate times a milli-unit value overstates by 1000.Blast radius today is staging only. That charges component is included by staging overlays alone, so production emits these metrics with no rates attached and no invoice moves. Worth settling before production gets rates.
One immutability constraint to plan around:
spec.metrics[].name,kindandunitare immutable once the ServiceConfiguration is Published, so a unit correction ships as a new metric name rather than an edit.https://claude.ai/code/session_01N7JuqnR9kgvHLuQnNcc5qQ
JoseSzycho commentedon Sep 3, 2026
I'm currently working into this
Merge pull request #318 from datum-cloud/fix-metrics-units