usage_records — one metered observation per (deployment, resource,
period, granularity). resource_kind is the BILLING kind (droplet_hours,
spaces_gb_month, ...) so it joins straight to plan_items. sub_attribution
jsonb column present from day one for forward-compat (sub-billing) but
never written in phase 3.
metering_config — per-billing-kind granularity (daily default; flip to
hourly when fine-grained billing is wanted) + retention_days.
ArcadiaCloud.Metering:
- meter_day/1 — walks every deployment-attributed resource, maps its
inventory kind to a billing kind, records that day's usage. Idempotent
via the unique (deployment, resource, period, granularity) index.
- usage_for_period/3 — SUM(qty) GROUP BY billing kind over a date range,
returns the %{kind => qty} map the quote engine takes as :usage.
Uniform accrual model so everything downstream is just SUM(qty):
- droplet -> droplet_hours, 24/day when active (0 when off)
- spaces_bucket / snapshot / droplet_backup -> *_gb_month, size/days so
the month sums to GB-months
- dns_zone -> dns_zones, 1/days so the month sums to the zone count
MeteringWorker — Oban cron 01:10 UTC daily, meters the previous
complete day.
Smoke verified: a pilot deployment with 2 droplets + 2 DNS zones + 3
snapshots attributed; 10 days metered -> 70 usage records; aggregation
gave droplet_hours 480 (2x24x10), dns_zones 0.65 (2x10/31),
snapshot_gb_month 15.48; fed into the quote engine against Studio — all
within allowance so $50 base, no overage (correct for a light partial
month).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
22 lines
553 B
Elixir
22 lines
553 B
Elixir
defmodule ArcadiaCloud.Sync.MeteringWorker do
|
|
@moduledoc """
|
|
Daily metering job — meters the previous (complete) UTC day for every
|
|
deployment-attributed resource. Runs early each day so the day being
|
|
metered is fully over.
|
|
"""
|
|
|
|
use Oban.Worker, queue: :metering, max_attempts: 3
|
|
|
|
require Logger
|
|
|
|
alias ArcadiaCloud.Metering
|
|
|
|
@impl Oban.Worker
|
|
def perform(_job) do
|
|
date = Date.add(Date.utc_today(), -1)
|
|
count = Metering.meter_day(date)
|
|
Logger.info("[metering] metered #{date}: #{count} usage records")
|
|
:ok
|
|
end
|
|
end
|