g1t/services/billing/src/margin.rs

1,891 lines87,117 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1//! What g1t earns on each thing it sells, measured against what
2//! Cloudflare actually charged for it.
3//!
4//! Once a day, after `costs` has read Cloudflare's bill, the reconciler
5//! puts three figures side by side for every day and each of g1t's
6//! products (a "bucket": sandboxes, deployments, git, repository storage,
7//! …):
8//!
9//! 1. **What Cloudflare charged**: the day's cost lines `cost_map` gives
10//! the bucket.
11//! 2. **What g1t's meters recorded**: the cost on the ledger's entries for
12//! it (the price book's cost at the time) and, where a mapping names
13//! one, g1t's own count of the same units (git operations).
14//! 3. **What customers were charged**: the entries' value at price, before
15//! the plan's included usage, a trial or a pool paid part of it; and of
16//! that, what workspaces paid. Month-end meters (git, storage, scans,
17//! embeddings, the actions cache) come from daily snapshots of what they
18//! had come to (`pending_days`). The plan's price is the `platform`
19//! bucket's: the plan pays for running g1t.
20//!
21//! From those: margin per product (value against cost) and for all of g1t
22//! (money in against every cost); drift (counts or costs that disagree past
23//! a mapping's threshold, and leaks: cost with no revenue, or a Cloudflare
24//! meter no one mapped); each workspace's cost, Cloudflare's figure shared
25//! out by each workspace's own meters; and price proposals when a unit's
26//! real cost has moved (`pricing`). Alerts go to staff by email and as a
27//! banner in sudo. See docs/BILLING_OPERATIONS.md.
28
29use std::collections::{BTreeMap, BTreeSet};
30
31use g1t_contracts::billing::*;
32use g1t_contracts::{FailureCode, Outcome, new_id};
33use g1t_contracts::time::rfc3339;
34use g1t_kit::now_ms;
35use serde::{Deserialize, Serialize};
36use worker::wasm_bindgen::JsValue;
37use worker::{Env, Result};
38
39use crate::Billing;
40use crate::costs::{self, ARTIFACTS_OPERATIONS, DAY_MS, Rule, SOURCE_ARTIFACTS, SOURCE_BILLABLE, UNMAPPED};
41
42/// Buckets that are the cost of running g1t, paid by the plan rather than
43/// sold by the unit: never a leak for having no revenue of their own.
44pub(crate) const OVERHEAD: [&str; 1] = ["platform"];
45/// Buckets Cloudflare does not bill: their cost is g1t's own figure.
46pub(crate) const NOT_CLOUDFLARE: [&str; 1] = ["models"];
47/// The days drift is judged over.
48const DRIFT_DAYS: u64 = 7;
49/// The days a workspace's cost is set against its revenue.
50const ANOMALY_DAYS: u64 = 30;
51/// The days a unit's cost is measured over.
52const MEASURE_DAYS: u64 = 30;
53/// Fewer of g1t's units than this say nothing about cost per unit.
54const MIN_UNITS: f64 = 1_000.0;
55/// An open alert is emailed again after this long.
56const REMIND_MS: u64 = 7 * DAY_MS;
57
58// ---------------------------------------------------------------------
59// The arithmetic, apart from the database so it can be tested.
60// ---------------------------------------------------------------------
61
62/// One of g1t's products on one day.
63#[derive(Clone, Debug, Default, PartialEq)]
64pub(crate) struct ProductDay {
65 pub day: String,
66 pub bucket: String,
67 /// What Cloudflare charged g1t, in millionths of a dollar.
68 pub cf_cost_micros: i64,
69 /// What g1t's meters recorded it cost (the price book's cost).
70 pub own_cost_micros: i64,
71 /// What customers were charged for it at price, before what paid.
72 pub value_micros: i64,
73 /// Of that, what workspaces paid themselves.
74 pub cash_micros: i64,
75 /// Units Cloudflare counted and units g1t counted, where a mapping
76 /// says they are the same units.
77 pub cf_quantity: f64,
78 pub own_quantity: f64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it79 /// Of `cost()`, what went on usage g1t gave away (the workspaces'
80 /// `WorkspaceDay::given`, added up).
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running81 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily82}
83
84impl ProductDay {
85 /// What it cost: Cloudflare's figure where Cloudflare bills it, else
86 /// g1t's own (models are billed by their providers, through the gateway).
87 pub fn cost(&self) -> i64 {
88 if NOT_CLOUDFLARE.contains(&self.bucket.as_str()) { self.own_cost_micros } else { self.cf_cost_micros }
89 }
90}
91
92/// A line of Cloudflare's bill, as stored.
93#[derive(Clone, Debug, Deserialize)]
94pub(crate) struct LineRow {
95 pub day: String,
96 pub source: String,
97 pub product: String,
98 pub meter: String,
99 pub quantity: f64,
100 pub cost_usd: f64,
101}
102
103/// A count of g1t's own, as stored.
104#[derive(Clone, Debug, Deserialize)]
105pub(crate) struct OwnRow {
106 pub day: String,
107 pub meter: String,
108 pub workspace: String,
109 pub quantity: f64,
110}
111
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running112/// What g1t gave away, by why: its own comped workspaces, free use (a
113/// free period, free allowances, overruns g1t covered), the trial, and the
114/// open-source pool. The Team plan's included usage is paid for by the
115/// plan's price, so it is sold, not given.
116#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
117pub(crate) struct Given {
118 pub comped: i64,
119 pub free: i64,
120 pub trial: i64,
121 pub pool: i64,
122}
123
124impl Given {
125 pub fn total(&self) -> i64 {
126 self.comped + self.free + self.trial + self.pool
127 }
128
129 fn add(&mut self, other: &Given) {
130 self.comped += other.comped;
131 self.free += other.free;
132 self.trial += other.trial;
133 self.pool += other.pool;
134 }
135
136 /// The same shares of `cost` as these are of `value`, at most all of it.
137 fn of(&self, cost: i64, value: i64) -> Given {
138 let total = self.total();
139 if value <= 0 || cost <= 0 || total <= 0 {
140 return Given::default();
141 }
142 let given = cost as i128 * total.min(value) as i128 / value as i128;
143 let part = |x: i64| (given * x.max(0) as i128 / total as i128) as i64;
144 Given { comped: part(self.comped), free: part(self.free), trial: part(self.trial), pool: part(self.pool) }
145 }
146}
147
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily148/// What a workspace was charged for one key on one day.
149#[derive(Clone, Debug, Default, PartialEq)]
150pub(crate) struct UsageRow {
151 pub day: String,
152 pub workspace: String,
153 /// A ledger task (or `builds`), a month-end source, or `plan`.
154 pub key: String,
155 pub value: i64,
156 pub cash: i64,
157 pub cost: i64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it158 /// Of `value`, what g1t gave away: all of it for g1t's own (comped)
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running159 /// workspaces and in a free period, else what the trial and the pool
160 /// paid and the overruns g1t covered.
161 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily162}
163
164/// One workspace's share of a product's cost on one day.
165#[derive(Clone, Debug, PartialEq)]
166pub(crate) struct WorkspaceDay {
167 pub day: String,
168 pub workspace: String,
169 pub bucket: String,
170 pub cost: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead171 /// What the workspace paid in cash.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily172 pub revenue: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead173 /// What its usage was priced at, whoever paid for it.
174 pub value: i64,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running175 /// Of `cost`, the part g1t gave away: all of it for a comped workspace
176 /// or one with nothing priced that day (free use), else the cost times
177 /// the shares of its usage that day that g1t paid for.
178 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily179}
180
181fn micros(dollars: f64) -> i64 {
182 (dollars * 1_000_000.0).round() as i64
183}
184
185/// Puts the day's bill, g1t's counts and what customers were charged side
186/// by side, a row per day and bucket, and shares each bucket's cost out
187/// to workspaces.
188pub(crate) fn fold(
189 rules: &[Rule],
190 revenue_map: &BTreeMap<String, String>,
191 lines: &[LineRow],
192 own: &[OwnRow],
193 usage: &[UsageRow],
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running194 internal: &BTreeSet<String>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily195) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
196 let mut days: BTreeMap<(String, String), ProductDay> = BTreeMap::new();
197 let entry = |day: &str, bucket: &str| -> ProductDay {
198 ProductDay { day: day.to_owned(), bucket: bucket.to_owned(), ..ProductDay::default() }
199 };
200 // Which of g1t's own meters count each bucket's units.
201 let mut own_meters: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new();
202 for rule in rules {
203 if let Some(meter) = &rule.own_meter {
204 own_meters.entry(rule.bucket.as_str()).or_default().insert(meter.as_str());
205 }
206 }
207 let mut events: BTreeMap<(String, String), f64> = BTreeMap::new();
208 for line in lines {
209 let rule = costs::classify(rules, &line.product, &line.meter);
210 let bucket = rule.map_or(UNMAPPED, |r| r.bucket.as_str());
211 let key = (line.day.clone(), bucket.to_owned());
212 if line.source == SOURCE_ARTIFACTS {
213 // What Artifacts counted: operations only, and only where the
214 // bill does not count them itself.
215 if ARTIFACTS_OPERATIONS.contains(&line.meter.as_str()) {
216 *events.entry(key).or_default() += line.quantity;
217 }
218 continue;
219 }
220 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
221 row.cf_cost_micros += micros(line.cost_usd);
222 if line.source == SOURCE_BILLABLE && rule.is_some_and(|r| r.own_meter.is_some()) {
223 row.cf_quantity += line.quantity;
224 }
225 }
226 for (key, quantity) in events {
227 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
228 if row.cf_quantity == 0.0 {
229 row.cf_quantity = quantity;
230 }
231 }
232 // g1t's own counts of the same units, by bucket and by workspace.
233 let mut own_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
234 // Cloudflare's own count by workspace, where it gives one
235 // (`cloudflare_<bucket>`): the best way to share its cost.
236 let mut cf_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
237 for count in own {
238 if let Some(bucket) = count.meter.strip_prefix("cloudflare_") {
239 cf_by.entry((count.day.clone(), bucket.to_owned())).or_default().push((count.workspace.clone(), count.quantity));
240 continue;
241 }
242 for (bucket, meters) in &own_meters {
243 if meters.contains(count.meter.as_str()) {
244 let key = (count.day.clone(), (*bucket).to_owned());
245 days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1)).own_quantity += count.quantity;
246 own_by.entry(key).or_default().push((count.workspace.clone(), count.quantity));
247 }
248 }
249 }
250 // What customers were charged.
251 let bucket_of = |key: &str| revenue_map.get(key).cloned().unwrap_or_else(|| "models".to_owned());
252 let mut value_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
253 let mut cost_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
254 let mut revenue: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Margin alerts measure what is sold, and say dollars when a percentage would mislead255 let mut valued: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily256 let mut active: BTreeMap<String, Vec<(String, f64)>> = BTreeMap::new();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running257 let mut gave: BTreeMap<(String, String), (Given, i64)> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily258 for u in usage {
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it259 let g = gave.entry((u.day.clone(), u.workspace.clone())).or_default();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running260 g.0.add(&u.given);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it261 g.1 += u.value;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily262 let bucket = bucket_of(&u.key);
263 let key = (u.day.clone(), bucket.clone());
264 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
265 row.own_cost_micros += u.cost;
266 row.value_micros += u.value;
267 row.cash_micros += u.cash;
268 value_by.entry(key.clone()).or_default().push((u.workspace.clone(), u.value as f64));
269 cost_by.entry(key).or_default().push((u.workspace.clone(), u.cost as f64));
Margin alerts measure what is sold, and say dollars when a percentage would mislead270 *revenue.entry((u.day.clone(), u.workspace.clone(), bucket.clone())).or_default() += u.cash;
271 *valued.entry((u.day.clone(), u.workspace.clone(), bucket)).or_default() += u.value;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily272 active.entry(u.day.clone()).or_default().push((u.workspace.clone(), u.value.max(u.cost) as f64));
273 }
274 // Each bucket's cost shared out: by Cloudflare's own count per
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running275 // workspace, else by g1t's own count of its units, else by what its
276 // usage cost (so free use carries its own cost), else by what it was
277 // charged; running g1t, and what no one mapped, by each workspace's
278 // share of all usage that day.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily279 let mut shares: BTreeMap<(String, String, String), i64> = BTreeMap::new();
280 for ((day, bucket), row) in &days {
281 let key = (day.clone(), bucket.clone());
282 let weigh = |m: &BTreeMap<(String, String), Vec<(String, f64)>>| m.get(&key).filter(|w| w.iter().any(|(_, v)| *v > 0.0)).cloned();
283 let weights = if OVERHEAD.contains(&bucket.as_str()) || bucket == UNMAPPED {
284 active.get(day).cloned()
285 } else {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running286 weigh(&cf_by).or_else(|| weigh(&own_by)).or_else(|| weigh(&cost_by)).or_else(|| weigh(&value_by)).or_else(|| active.get(day).cloned())
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily287 };
288 for (workspace, micros) in attribute(row.cost(), &weights.unwrap_or_default()) {
289 *shares.entry((day.clone(), workspace, bucket.clone())).or_default() += micros;
290 }
291 }
292 let keys: BTreeSet<(String, String, String)> = shares.keys().chain(revenue.keys()).cloned().collect();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it293 let workspaces: Vec<WorkspaceDay> = keys
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily294 .into_iter()
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it295 .map(|(day, workspace, bucket)| {
296 let cost = shares.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running297 // The day's shares given away apply to every bucket, so a
298 // comped workspace's part of running g1t is given too. A
299 // workspace with nothing priced that day used g1t for free.
300 let given = if internal.contains(&workspace) {
301 Given { comped: cost, ..Given::default() }
302 } else {
303 match gave.get(&(day.clone(), workspace.clone())) {
304 Some((given, value)) if *value > 0 => given.of(cost, *value),
305 _ => Given { free: cost.max(0), ..Given::default() },
306 }
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it307 };
308 WorkspaceDay {
309 cost,
310 revenue: revenue.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
311 value: valued.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
312 given,
313 day,
314 workspace,
315 bucket,
316 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily317 })
318 .collect();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it319 for w in &workspaces {
320 if let Some(row) = days.get_mut(&(w.day.clone(), w.bucket.clone())) {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running321 row.given.add(&w.given);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it322 }
323 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily324 (days.into_values().collect(), workspaces)
325}
326
327/// A month-end source's day, from the snapshots of what it had come to:
328/// each day's figure less the day before's in the same month (the first
329/// day of a month, or the first snapshot, is its own).
330pub(crate) fn pending_deltas(snapshots: &[(String, String, String, i64, i64)]) -> Vec<UsageRow> {
331 // (day, workspace, source, cost, charge), any order.
332 let mut sorted = snapshots.to_vec();
333 sorted.sort_by(|a, b| (&a.1, &a.2, &a.0).cmp(&(&b.1, &b.2, &b.0)));
334 let mut out = Vec::new();
335 let mut previous: Option<&(String, String, String, i64, i64)> = None;
336 for snap in &sorted {
337 let (day, workspace, source, cost, charge) = snap;
338 let (before_cost, before_charge) = match previous {
339 Some(p) if p.1 == *workspace && p.2 == *source && p.0[..7] == day[..7] => (p.3, p.4),
340 _ => (0, 0),
341 };
342 let (cost, charge) = ((cost - before_cost).max(0), (charge - before_charge).max(0));
343 if cost > 0 || charge > 0 {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running344 out.push(UsageRow { day: day.clone(), workspace: workspace.clone(), key: source.clone(), value: charge, cash: charge, cost, given: Given::default() });
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily345 }
346 previous = Some(snap);
347 }
348 out
349}
350
351/// Margin as a share of what was charged, in percent; None when nothing was.
352pub(crate) fn margin_percent(revenue_micros: i64, cost_micros: i64) -> Option<f64> {
353 (revenue_micros > 0).then(|| (revenue_micros - cost_micros) as f64 * 100.0 / revenue_micros as f64)
354}
355
356/// How far `ours` is from `theirs`, in percent of theirs; None when theirs
357/// is nothing.
358pub(crate) fn delta_percent(ours: f64, theirs: f64) -> Option<f64> {
359 (theirs > 0.0).then(|| (ours - theirs) * 100.0 / theirs)
360}
361
362#[derive(Clone, Copy, Debug, PartialEq, Eq)]
363pub(crate) enum DriftKind {
364 /// g1t counted a different number of units than Cloudflare did.
365 Count,
366 /// What Cloudflare charged differs from what the price book says the
367 /// same usage cost.
368 Cost,
369 /// Cloudflare charged for something nothing charges customers for.
370 Leak,
371}
372
373impl DriftKind {
374 pub fn as_str(self) -> &'static str {
375 match self {
376 DriftKind::Count => "count",
377 DriftKind::Cost => "cost",
378 DriftKind::Leak => "leak",
379 }
380 }
381}
382
383#[derive(Clone, Debug, PartialEq)]
384pub(crate) struct Drift {
385 pub bucket: String,
386 pub kind: DriftKind,
387 pub ours: f64,
388 pub cloudflare: f64,
389 pub delta_percent: Option<f64>,
390}
391
392/// Drift over a window for one bucket: counts more than `threshold`
393/// percent apart, a bill that far from the price book's cost of the same
394/// usage, and cost with nothing charged for it. Under `min_cost_micros`
395/// in all, cost says nothing.
396pub(crate) fn drifts(bucket: &str, days: &[ProductDay], threshold: f64, counted: bool, min_cost_micros: i64) -> Vec<Drift> {
397 let overhead = OVERHEAD.contains(&bucket);
398 let sum = |f: &dyn Fn(&ProductDay) -> f64| days.iter().map(f).sum::<f64>();
399 let cf_cost = sum(&|d| d.cf_cost_micros as f64);
400 let own_cost = sum(&|d| d.own_cost_micros as f64);
401 let value = sum(&|d| d.value_micros as f64);
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift402 // Counts are compared from the first day g1t counted: before its meter
403 // was deployed there is only Cloudflare's side. A meter that never
404 // counted anything is compared over every day, so it still shows.
405 let first_counted = days.iter().filter(|d| d.own_quantity > 0.0).map(|d| d.day.as_str()).min();
406 let compared = |d: &&ProductDay| first_counted.is_none_or(|from| d.day.as_str() >= from);
407 let (cf_quantity, own_quantity) = days.iter().filter(compared).fold((0.0, 0.0), |(cf, own), d| (cf + d.cf_quantity, own + d.own_quantity));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily408 let mut out = Vec::new();
409 if counted && cf_quantity > 0.0 {
410 let delta = delta_percent(own_quantity, cf_quantity);
411 if delta.is_some_and(|d| d.abs() > threshold) {
412 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Count, ours: own_quantity, cloudflare: cf_quantity, delta_percent: delta });
413 }
414 }
415 let enough = cf_cost.max(own_cost) >= min_cost_micros as f64;
416 if enough && !overhead && cf_cost > 0.0 && own_cost > 0.0 && !NOT_CLOUDFLARE.contains(&bucket) {
417 let delta = delta_percent(own_cost, cf_cost);
418 if delta.is_some_and(|d| d.abs() > threshold) {
419 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: cf_cost, delta_percent: delta });
420 }
421 }
422 if !overhead && cf_cost >= min_cost_micros as f64 && value <= 0.0 {
423 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Leak, ours: value, cloudflare: cf_cost, delta_percent: None });
424 }
425 out
426}
427
428/// When the last `days` in a row (each with enough cost to say something)
429/// were all under the floor: the first of them and the worst margin.
430/// Each item is a day's (day, revenue, cost).
431pub(crate) fn breach(series: &[(String, i64, i64)], floor_percent: f64, days: usize, min_cost_micros: i64) -> Option<(String, f64)> {
432 if days == 0 || series.len() < days {
433 return None;
434 }
435 let tail = &series[series.len() - days..];
436 let mut worst = f64::INFINITY;
437 for (_, revenue, cost) in tail {
438 if *cost < min_cost_micros {
439 return None;
440 }
441 let margin = margin_percent(*revenue, *cost).unwrap_or(-100.0);
442 if margin >= floor_percent {
443 return None;
444 }
445 worst = worst.min(margin);
446 }
447 Some((tail[0].0.clone(), worst))
448}
449
450/// `total` shared out in proportion to `weights`, in whole millionths that
451/// add up to it exactly (largest remainder first). Nothing to share, or no
452/// weight, shares nothing.
453pub(crate) fn attribute(total: i64, weights: &[(String, f64)]) -> Vec<(String, i64)> {
454 let mut merged: BTreeMap<String, f64> = BTreeMap::new();
455 for (key, w) in weights {
456 *merged.entry(key.clone()).or_default() += w.max(0.0);
457 }
458 let sum: f64 = merged.values().sum();
459 if total <= 0 || sum <= 0.0 {
460 return Vec::new();
461 }
462 let mut shares: Vec<(String, i64, f64)> = merged
463 .into_iter()
464 .map(|(key, w)| {
465 let exact = total as f64 * w / sum;
466 (key, exact.floor() as i64, exact - exact.floor())
467 })
468 .collect();
469 let mut left = total - shares.iter().map(|s| s.1).sum::<i64>();
470 let mut order: Vec<usize> = (0..shares.len()).collect();
471 order.sort_by(|a, b| shares[*b].2.total_cmp(&shares[*a].2).then(shares[*a].0.cmp(&shares[*b].0)));
472 for index in order {
473 if left <= 0 {
474 break;
475 }
476 shares[index].1 += 1;
477 left -= 1;
478 }
479 shares.into_iter().filter(|s| s.1 > 0).map(|(key, micros, _)| (key, micros)).collect()
480}
481
482/// Workspaces that cost g1t more than `factor` times what they paid, with
483/// at least `floor_micros` of cost: each (workspace, cost, revenue), the
484/// biggest gap first.
Models' margin read -14%: usage nothing paid for is valued at price, not $0485/// What a day's usage was worth at price. g1t's own workspaces are valued
486/// at price. So is usage nothing paid for, neither charged nor drawn from
487/// the plan, a trial, a pool or a gift (a free period): it was given away at
488/// its price, not sold for nothing. Anything paid keeps what it was paid, so
489/// a discount still shows as one.
490pub(crate) fn usage_value(internal: bool, cost: i64, paid: i64, margin_percent: u32) -> i64 {
491 if internal || (paid == 0 && cost > 0) {
492 return crate::credits::with_margin(cost, margin_percent);
493 }
494 paid
495}
496
Margin alerts measure what is sold, and say dollars when a percentage would mislead497/// What the overall alert says: the money as money, and a percentage only
498/// while there is enough coming in for one to mean something (a few cents
499/// against dollars of cost reads as -8000%).
500pub(crate) fn overall_detail(took: i64, spent: i64, days: usize, floor: f64, worst: f64) -> String {
501 if took < 1_000_000 * days as i64 {
502 return format!(
503 "All of g1t, comped workspaces left out: took in {} against {} of Cloudflare's bill over {days} days.",
504 dollars(took),
505 dollars(spent)
506 );
507 }
508 format!("All of g1t, comped workspaces left out: money in against Cloudflare's bill under {floor:.0}% for {days} days running, as low as {worst:.1}%.")
509}
510
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily511pub(crate) fn anomalies(rows: &[(String, i64, i64)], factor: f64, floor_micros: i64) -> Vec<(String, i64, i64)> {
512 let mut out: Vec<(String, i64, i64)> = rows
513 .iter()
514 .filter(|(_, cost, revenue)| *cost >= floor_micros && *cost as f64 > *revenue as f64 * factor)
515 .cloned()
516 .collect();
517 out.sort_by(|a, b| (b.1 - b.2).cmp(&(a.1 - a.2)).then(a.0.cmp(&b.0)));
518 out
519}
520
521/// Cloudflare's marginal rate for one of its units: the median over the
522/// charged days of cost over quantity, in dollars. None while the included
523/// amounts still cover it. Each item is a day's (quantity, cost).
524pub(crate) fn billed_rate(days: &[(f64, f64)]) -> Option<f64> {
525 let mut rates: Vec<f64> = days.iter().filter(|(q, c)| *q > 0.0 && *c > 0.0).map(|(q, c)| c / q).collect();
526 if rates.is_empty() {
527 return None;
528 }
529 rates.sort_by(f64::total_cmp);
530 Some(rates[rates.len() / 2])
531}
532
533/// What one of g1t's units costs, from Cloudflare's rate per its own unit
534/// and how many of Cloudflare's units each of g1t's took: if Cloudflare
535/// counts three operations for every git operation g1t counts, a git
536/// operation costs three of Cloudflare's. None without enough of g1t's
537/// units to say.
538pub(crate) fn derived_unit_cost(rate_per_cf_unit: f64, cf_units: f64, own_units: f64) -> Option<f64> {
539 (own_units >= MIN_UNITS && cf_units > 0.0 && rate_per_cf_unit > 0.0).then(|| rate_per_cf_unit * cf_units / own_units)
540}
541
542/// How many units a price is per: `1,000 operations` → 1,000, `million
543/// requests` → 1,000,000, `second` → 1.
544pub(crate) fn unit_size(unit: &str) -> f64 {
545 let first = unit.split_whitespace().next().unwrap_or_default().replace(',', "");
546 match first.as_str() {
547 "million" => 1_000_000.0,
548 "thousand" => 1_000.0,
549 n => n.parse().unwrap_or(1.0),
550 }
551}
552
553fn day_before(day: &str, days: u64) -> String {
554 let ms = g1t_contracts::time::parse_rfc3339(&format!("{day}T00:00:00Z")).unwrap_or(0);
555 rfc3339(ms.saturating_sub(days * DAY_MS))[..10].to_owned()
556}
557
558/// Dollars to the cent from a dollar up, finer below: `$17.02`, `$0.063`.
559fn dollars(micros: i64) -> String {
560 if micros.abs() >= 1_000_000 {
561 let cents = (micros as f64 / 10_000.0).round() as i64;
562 format!("{}${}.{:02}", if cents < 0 { "-" } else { "" }, cents.abs() / 100, cents.abs() % 100)
563 } else {
564 crate::features::dollars(micros)
565 }
566}
567
568/// The days a plan payment is spread over.
569const PLAN_DAYS: u64 = 30;
570
571/// `micros` paid on `day` spread evenly over `days` days from it, in
572/// whole micros that add up to it (the first days take the remainder).
573pub(crate) fn spread(day: &str, micros: i64, days: u64) -> Vec<(String, i64)> {
574 if micros <= 0 || days == 0 {
575 return Vec::new();
576 }
577 let start = g1t_contracts::time::parse_rfc3339(&format!("{}T00:00:00Z", &day[..10.min(day.len())])).unwrap_or(0);
578 let each = micros / days as i64;
579 let rest = micros % days as i64;
580 (0..days)
581 .map(|n| (rfc3339(start + n * DAY_MS)[..10].to_owned(), each + i64::from((n as i64) < rest)))
582 .collect()
583}
584
585// ---------------------------------------------------------------------
586// The daily run, and what sudo reads.
587// ---------------------------------------------------------------------
588
589#[derive(Serialize)]
590struct Mail<'a> {
591 to: &'a str,
592 from: &'a str,
593 subject: &'a str,
594 text: String,
595 html: String,
596}
597
598fn escape(text: &str) -> String {
599 text.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;").replace('"', "&quot;")
600}
601
602/// Emails staff through Cloudflare Email Sending, the `EMAIL` binding.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays603pub(crate) async fn email_staff(env: &Env, to: &str, subject: &str, lines: &[String]) -> Result<()> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily604 let link = "https://sudo.g1t.sh/costs";
605 let text = format!("{}\n\nCosts & margin: {link}\n\nSent by g1t-billing's margin guard (COSTS_ALERT_EMAIL).\n", lines.join("\n\n"));
606 let mut html = String::from("<div style=\"font-family:system-ui,sans-serif;max-width:560px;margin:0 auto;padding:24px 16px;color:#16150f\">");
607 for line in lines {
608 html.push_str(&format!("<p style=\"font-size:15px;line-height:1.6\">{}</p>", escape(line)));
609 }
610 html.push_str(&format!(
611 "<p><a href=\"{link}\">Open Costs &amp; margin in sudo</a></p><p style=\"font-size:13px;color:#6e6a5e\">Sent by g1t-billing's margin guard (COSTS_ALERT_EMAIL).</p></div>"
612 ));
613 let mail = Mail { to, from: "g1t <noreply@g1t.sh>", subject, text, html };
614 let binding = g1t_kit::js::binding(env, "EMAIL")?;
615 g1t_kit::js::call(&binding, "send", &[g1t_kit::js::to_js(&mail)?]).await?;
616 Ok(())
617}
618
619#[derive(Deserialize)]
620struct AlertRow {
621 id: String,
622 kind: String,
623 subject: String,
624 detail: String,
625 since: String,
626 opened_at: String,
627 emailed_at: Option<String>,
628}
629
630impl From<AlertRow> for MarginAlert {
631 fn from(r: AlertRow) -> Self {
632 MarginAlert { id: r.id, kind: r.kind, subject: r.subject, detail: r.detail, since: r.since, opened_at: r.opened_at, emailed_at: r.emailed_at }
633 }
634}
635
636#[derive(Deserialize)]
637struct MarginRow {
638 day: String,
639 bucket: String,
640 cf_cost_micros: i64,
641 own_cost_micros: i64,
642 value_micros: i64,
643 cash_micros: i64,
644 cf_quantity: f64,
645 own_quantity: f64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it646 #[serde(default)]
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running647 given_comped_micros: Option<i64>,
648 #[serde(default)]
649 given_free_micros: Option<i64>,
650 #[serde(default)]
651 given_trial_micros: Option<i64>,
652 #[serde(default)]
653 given_pool_micros: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily654}
655
656impl From<MarginRow> for ProductDay {
657 fn from(r: MarginRow) -> Self {
658 ProductDay {
659 day: r.day,
660 bucket: r.bucket,
661 cf_cost_micros: r.cf_cost_micros,
662 own_cost_micros: r.own_cost_micros,
663 value_micros: r.value_micros,
664 cash_micros: r.cash_micros,
665 cf_quantity: r.cf_quantity,
666 own_quantity: r.own_quantity,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running667 given: Given {
668 comped: r.given_comped_micros.unwrap_or(0),
669 free: r.given_free_micros.unwrap_or(0),
670 trial: r.given_trial_micros.unwrap_or(0),
671 pool: r.given_pool_micros.unwrap_or(0),
672 },
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily673 }
674 }
675}
676
677impl Billing {
678 /// The day's work: read Cloudflare's bill and g1t's own counts,
679 /// reconcile, look for drift, measure unit costs, apply prices whose
680 /// day has come, and raise or clear alerts.
681 pub(crate) async fn costs_daily(&self, env: &Env, keeper: &crate::keeper::Keeper) -> Result<CostsRun> {
682 let mut run = CostsRun::default();
683 let (since, until) = match self.read_cloudflare(keeper, &mut run.problems).await? {
684 Some((since, until, lines)) => {
685 run.lines = lines;
686 (since, until)
687 }
688 // Without the bill, still reconcile what g1t knows itself, over
689 // the same days the bill would be read for.
690 None => {
691 #[derive(Deserialize)]
692 struct Last {
693 day: Option<String>,
694 }
695 let last = self.db.prepare("SELECT MAX(day) AS day FROM margin_days").first::<Last>(None).await?.and_then(|l| l.day);
696 costs::window(last.as_deref(), now_ms())
697 }
698 };
699 if let Err(error) = self.count_own(&since, &until).await {
700 run.problems.push(format!("g1t's own counts could not be read: {error}"));
701 }
702 self.snapshot_pending(&until).await?;
Models' margin read -14%: usage nothing paid for is valued at price, not $0703 // Reconciled over the whole window sudo shows, not only the days the
704 // bill was read for: it reads only what is already kept, so a change
705 // in how a day is valued reaches every day shown at the next run.
706 let window = day_before(&until, costs::BACKFILL_DAYS - 1);
707 let reconcile_from = if window < since { window } else { since.clone() };
708 run.days = self.reconcile_range(&reconcile_from, &until).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily709 let drift = self.find_drift(&until).await?;
710 run.proposals = self.measure_units(&until).await?;
711 self.apply_due_versions().await?;
712 run.alerts = self.raise_alerts(env, &until, &drift).await?;
713 if let Some(identity) = &self.identity
714 && let Err(error) = self.tell_owners_of_rises(identity).await
715 {
716 run.problems.push(format!("owners could not be told of a price rise: {error}"));
717 }
718 for problem in &run.problems {
719 worker::console_warn!("costs: {problem}");
720 }
721 Ok(run)
722 }
723
724 /// What each month-end source had come to by the end of `day`.
725 async fn snapshot_pending(&self, day: &str) -> Result<()> {
726 self.db
727 .prepare(
728 "INSERT INTO pending_days (day, workspace, source, cost_micros, charge_micros)
729 SELECT ?1, workspace, source, COALESCE(cost_micros, 0), COALESCE(charge_micros, 0) FROM pending_usage WHERE month = ?2
730 ON CONFLICT (day, workspace, source) DO UPDATE SET cost_micros = excluded.cost_micros, charge_micros = excluded.charge_micros",
731 )
732 .bind(&[day.into(), day[..7].into()])?
733 .run()
734 .await?;
735 Ok(())
736 }
737
738 /// What customers were charged on the days, by workspace and key.
739 async fn usage_rows(&self, since: &str, until: &str) -> Result<Vec<UsageRow>> {
740 #[derive(Deserialize)]
741 struct Row {
742 day: String,
743 workspace: String,
744 key: String,
745 internal: i64,
746 own_provider: i64,
747 cash: Option<i64>,
748 drawn: Option<i64>,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running749 trial: Option<i64>,
750 oss: Option<i64>,
751 covered: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily752 cost: Option<i64>,
753 }
754 let charged_here = crate::storage::CHARGED_HERE.iter().map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", ");
755 let end = format!("{until}T23:59:59.999Z");
756 let rows = self
757 .db
758 .prepare(format!(
759 "SELECT substr(created_at, 1, 10) AS day, workspace,
760 CASE WHEN task = 'deployments' AND reference LIKE 'deploy/%' THEN 'builds' ELSE COALESCE(task, 'other') END AS key,
761 CASE WHEN workspace IN ({internal}) THEN 1 ELSE 0 END AS internal,
762 CASE WHEN billed_to = 'workspace' THEN 1 ELSE 0 END AS own_provider,
763 -SUM(amount_micros) AS cash,
764 SUM(COALESCE(credit_micros, 0) + COALESCE(trial_micros, 0) + COALESCE(oss_micros, 0) + COALESCE(given_micros, 0)) AS drawn,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running765 SUM(COALESCE(trial_micros, 0)) AS trial,
766 SUM(COALESCE(oss_micros, 0)) AS oss,
767 SUM(COALESCE(given_micros, 0)) AS covered,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily768 SUM(COALESCE(cost_micros, 0)) AS cost
769 FROM ledger
770 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND COALESCE(task, '') NOT IN ({charged_here})
771 GROUP BY 1, 2, 3, 4, 5",
772 internal = crate::sales::INTERNAL_SQL
773 ))
774 .bind(&[since.into(), end.as_str().into()])?
775 .all()
776 .await?
777 .results::<Row>()?;
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it778 let mut internal = BTreeSet::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily779 let mut out: Vec<UsageRow> = rows
780 .into_iter()
781 .map(|r| {
782 // A workspace's own model provider was paid there: no cost
783 // to g1t. g1t's own workspaces are valued at price.
784 let cost = if r.own_provider == 1 { 0 } else { r.cost.unwrap_or(0) };
785 let cash = r.cash.unwrap_or(0);
Models' margin read -14%: usage nothing paid for is valued at price, not $0786 let paid = cash + r.drawn.unwrap_or(0);
787 let value = usage_value(r.internal == 1, cost, paid, self.margin_percent);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running788 let given = if r.internal == 1 {
789 Given { comped: value, ..Given::default() }
790 } else if paid == 0 && cost > 0 {
791 Given { free: value, ..Given::default() }
792 } else {
793 Given { free: r.covered.unwrap_or(0), trial: r.trial.unwrap_or(0), pool: r.oss.unwrap_or(0), comped: 0 }
794 };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it795 if r.internal == 1 {
796 internal.insert(r.workspace.clone());
797 }
798 UsageRow { day: r.day, workspace: r.workspace, key: r.key, value, cash, cost, given }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily799 })
800 .collect();
801 // Month-end sources, from their daily snapshots.
802 #[derive(Deserialize)]
803 struct Snap {
804 day: String,
805 workspace: String,
806 source: String,
807 cost_micros: i64,
808 charge_micros: i64,
809 }
810 let snaps = self
811 .db
812 .prepare("SELECT day, workspace, source, cost_micros, charge_micros FROM pending_days WHERE day >= ?1 AND day <= ?2")
813 .bind(&[day_before(since, 1).into(), until.into()])?
814 .all()
815 .await?
816 .results::<Snap>()?
817 .into_iter()
818 .filter(|s| crate::storage::CHARGED_HERE.contains(&s.source.as_str()) || s.source == "domains")
819 .map(|s| (s.day, s.workspace, s.source, s.cost_micros, s.charge_micros))
820 .collect::<Vec<_>>();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it821 out.extend(pending_deltas(&snaps).into_iter().filter(|u| u.day.as_str() >= since).map(|mut u| {
822 if internal.contains(&u.workspace) {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running823 u.given = Given { comped: u.value, ..Given::default() };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it824 }
825 u
826 }));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily827 // The plan's price, spread over the 30 days it pays for, so a month's
828 // payment does not read as one very good day and 29 bad ones.
829 #[derive(Deserialize)]
830 struct Plan {
831 day: String,
832 workspace: String,
833 micros: Option<i64>,
834 }
835 let plans = self
836 .db
837 .prepare(
838 "SELECT substr(paid_at, 1, 10) AS day, workspace, SUM(amount_micros) AS micros FROM plan_payments
839 WHERE paid_at >= ?1 AND paid_at <= ?2 GROUP BY 1, 2",
840 )
841 .bind(&[day_before(since, PLAN_DAYS - 1).into(), end.as_str().into()])?
842 .all()
843 .await?
844 .results::<Plan>()?;
845 for p in plans {
846 for (day, micros) in spread(&p.day, p.micros.unwrap_or(0), PLAN_DAYS) {
847 if day.as_str() >= since && day.as_str() <= until {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running848 out.push(UsageRow { day, workspace: p.workspace.clone(), key: "plan".into(), value: micros, cash: micros, cost: 0, given: Given::default() });
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily849 }
850 }
851 }
852 Ok(out)
853 }
854
855 /// Reconciles the days and writes `margin_days` and `workspace_costs`.
856 async fn reconcile_range(&self, since: &str, until: &str) -> Result<u32> {
857 let rules = self.rules().await?;
858 #[derive(Deserialize)]
859 struct Map {
860 key: String,
861 bucket: String,
862 }
863 let revenue_map: BTreeMap<String, String> = self
864 .db
865 .prepare("SELECT key, bucket FROM revenue_map")
866 .all()
867 .await?
868 .results::<Map>()?
869 .into_iter()
870 .map(|m| (m.key, m.bucket))
871 .collect();
872 let lines = self
873 .db
874 .prepare("SELECT day, source, product, meter, quantity, cost_usd FROM cost_lines WHERE day >= ?1 AND day <= ?2")
875 .bind(&[since.into(), until.into()])?
876 .all()
877 .await?
878 .results::<LineRow>()?;
879 let own = self
880 .db
881 .prepare("SELECT day, meter, workspace, quantity FROM own_counts WHERE day >= ?1 AND day <= ?2")
882 .bind(&[since.into(), until.into()])?
883 .all()
884 .await?
885 .results::<OwnRow>()?;
886 let usage = self.usage_rows(since, until).await?;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running887 #[derive(Deserialize)]
888 struct Internal {
889 workspace: String,
890 }
891 let internal: BTreeSet<String> = self
892 .db
893 .prepare(format!("WITH i(workspace) AS ({}) SELECT DISTINCT workspace FROM i", crate::sales::INTERNAL_SQL))
894 .all()
895 .await?
896 .results::<Internal>()?
897 .into_iter()
898 .map(|i| i.workspace)
899 .collect();
900 let (days, workspaces) = fold(&rules, &revenue_map, &lines, &own, &usage, &internal);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily901 let now = rfc3339(now_ms());
902 self.db
903 .batch(vec![
904 self.db.prepare("DELETE FROM margin_days WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
905 self.db.prepare("DELETE FROM workspace_costs WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
906 ])
907 .await?;
908 for chunk in days.chunks(50) {
909 let mut statements = Vec::with_capacity(chunk.len());
910 for d in chunk {
911 statements.push(
912 self.db
913 .prepare(
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running914 "INSERT OR REPLACE INTO margin_days (day, bucket, cf_cost_micros, own_cost_micros, value_micros, cash_micros, cf_quantity, own_quantity, given_micros, given_comped_micros, given_free_micros, given_trial_micros, given_pool_micros, computed_at)
915 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily916 )
917 .bind(&[
918 d.day.as_str().into(),
919 d.bucket.as_str().into(),
920 (d.cf_cost_micros as f64).into(),
921 (d.own_cost_micros as f64).into(),
922 (d.value_micros as f64).into(),
923 (d.cash_micros as f64).into(),
924 d.cf_quantity.into(),
925 d.own_quantity.into(),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running926 (d.given.total() as f64).into(),
927 (d.given.comped as f64).into(),
928 (d.given.free as f64).into(),
929 (d.given.trial as f64).into(),
930 (d.given.pool as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily931 now.as_str().into(),
932 ])?,
933 );
934 }
935 self.db.batch(statements).await?;
936 }
937 for chunk in workspaces.chunks(50) {
938 let mut statements = Vec::with_capacity(chunk.len());
939 for w in chunk {
940 statements.push(
941 self.db
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it942 .prepare("INSERT OR REPLACE INTO workspace_costs (day, workspace, bucket, cost_micros, revenue_micros, value_micros, given_micros) VALUES (?, ?, ?, ?, ?, ?, ?)")
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily943 .bind(&[
944 w.day.as_str().into(),
945 w.workspace.as_str().into(),
946 w.bucket.as_str().into(),
947 (w.cost as f64).into(),
948 (w.revenue as f64).into(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead949 (w.value as f64).into(),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running950 (w.given.total() as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily951 ])?,
952 );
953 }
954 self.db.batch(statements).await?;
955 }
956 Ok(costs::days_between(since, until).len() as u32)
957 }
958
959 async fn margin_days(&self, since: &str, until: &str) -> Result<Vec<ProductDay>> {
960 Ok(self
961 .db
962 .prepare("SELECT * FROM margin_days WHERE day >= ?1 AND day <= ?2 ORDER BY day, bucket")
963 .bind(&[since.into(), until.into()])?
964 .all()
965 .await?
966 .results::<MarginRow>()?
967 .into_iter()
968 .map(ProductDay::from)
969 .collect())
970 }
971
972 /// Drift over the last week, written to `cost_drift` (replacing the
973 /// last run's), with unmapped Cloudflare meters as leaks.
974 async fn find_drift(&self, until: &str) -> Result<Vec<(Drift, String)>> {
975 let since = day_before(until, DRIFT_DAYS - 1);
976 let settings = self.cost_settings().await?;
977 let rules = self.rules().await?;
978 let days = self.margin_days(&since, until).await?;
979 let mut by: BTreeMap<String, Vec<ProductDay>> = BTreeMap::new();
980 for d in days {
981 by.entry(d.bucket.clone()).or_default().push(d);
982 }
983 let mut found = Vec::new();
984 for (bucket, days) in &by {
985 let bucket_rules: Vec<&Rule> = rules.iter().filter(|r| &r.bucket == bucket).collect();
986 let threshold = bucket_rules.iter().map(|r| r.drift_percent).fold(f64::INFINITY, f64::min);
987 let threshold = if threshold.is_finite() { threshold } else { 10.0 };
988 let counted = bucket_rules.iter().any(|r| r.own_meter.is_some());
989 for drift in drifts(bucket, days, threshold, counted, settings.min_daily_cost_micros) {
990 let title = costs::bucket_title(bucket);
991 let detail = match drift.kind {
992 DriftKind::Count => format!(
One operation mapping, owned by repos; billing reads it instead of keeping its own993 "{title}: g1t counted {}, Cloudflare {} over the last {DRIFT_DAYS} days ({:+.1}%). Customers are charged for what g1t counts; check what Cloudflare counts as a unit and change the repos service's operation_mapping (set_operation_mapping).",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily994 crate::features::thousands(drift.ours.max(0.0).round() as u64),
995 crate::features::thousands(drift.cloudflare.max(0.0).round() as u64),
996 drift.delta_percent.unwrap_or(0.0)
997 ),
998 DriftKind::Cost => format!(
999 "{title}: Cloudflare charged {} over the last {DRIFT_DAYS} days; the price book's cost of the same usage is {} ({:+.1}%). A price may be stale: see the proposals.",
1000 dollars(drift.cloudflare as i64),
1001 dollars(drift.ours as i64),
1002 drift.delta_percent.unwrap_or(0.0)
1003 ),
1004 DriftKind::Leak if bucket == UNMAPPED => {
1005 format!("Cloudflare charged {} for meters no mapping claims. Map them on Costs & margin.", dollars(drift.cloudflare as i64))
1006 }
1007 DriftKind::Leak => format!(
1008 "{title}: Cloudflare charged {} over the last {DRIFT_DAYS} days and customers were charged nothing for it.",
1009 dollars(drift.cloudflare as i64)
1010 ),
1011 };
1012 found.push((drift, detail));
1013 }
1014 }
1015 let now = rfc3339(now_ms());
1016 let mut statements = vec![self.db.prepare("DELETE FROM cost_drift")];
1017 for (drift, detail) in &found {
1018 statements.push(
1019 self.db
1020 .prepare("INSERT OR REPLACE INTO cost_drift (bucket, kind, ours, cloudflare, delta_percent, detail, found_at) VALUES (?, ?, ?, ?, ?, ?, ?)")
1021 .bind(&[
1022 drift.bucket.as_str().into(),
1023 drift.kind.as_str().into(),
1024 drift.ours.into(),
1025 drift.cloudflare.into(),
1026 drift.delta_percent.map_or(JsValue::NULL, JsValue::from),
1027 detail.as_str().into(),
1028 now.as_str().into(),
1029 ])?,
1030 );
1031 }
1032 self.db.batch(statements).await?;
1033 Ok(found)
1034 }
1035
1036 /// Unit costs from the bill for mappings that scale to g1t's own count
1037 /// (git operations), proposed to the price book.
1038 async fn measure_units(&self, until: &str) -> Result<u32> {
1039 #[derive(Deserialize)]
1040 struct Scaled {
1041 product: String,
1042 meter: String,
1043 price_meter: String,
1044 own_meter: String,
1045 unit: Option<String>,
1046 }
1047 let scaled = self
1048 .db
1049 .prepare(
1050 "SELECT m.product, m.meter, m.price_meter, m.own_meter, p.unit FROM cost_map m LEFT JOIN prices p ON p.meter = m.price_meter
1051 WHERE m.scale_to_own = 1 AND m.price_meter IS NOT NULL AND m.own_meter IS NOT NULL",
1052 )
1053 .all()
1054 .await?
1055 .results::<Scaled>()?;
1056 let since = day_before(until, MEASURE_DAYS - 1);
1057 let rules = self.rules().await?;
1058 let mut proposed = 0;
1059 for s in scaled {
1060 #[derive(Deserialize)]
1061 struct Day {
1062 product: String,
1063 meter: String,
1064 quantity: f64,
1065 cost_usd: f64,
1066 }
1067 let lines = self
1068 .db
1069 .prepare("SELECT product, meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND product = ?2 AND day >= ?3 AND day <= ?4")
1070 .bind(&[SOURCE_BILLABLE.into(), s.product.as_str().into(), since.as_str().into(), until.into()])?
1071 .all()
1072 .await?
1073 .results::<Day>()?;
1074 // Only the lines this very mapping claims.
1075 let mine: Vec<(f64, f64)> = lines
1076 .iter()
1077 .filter(|l| costs::classify(&rules, &l.product, &l.meter).is_some_and(|r| r.product == s.product && r.meter == s.meter))
1078 .map(|l| (l.quantity, l.cost_usd))
1079 .collect();
1080 let Some(rate) = billed_rate(&mine) else { continue };
1081 let cf_units: f64 = mine.iter().map(|(q, _)| q).sum();
1082 #[derive(Deserialize)]
1083 struct Own {
1084 total: Option<f64>,
1085 }
1086 let own_units = self
1087 .db
1088 .prepare("SELECT SUM(quantity) AS total FROM own_counts WHERE meter = ?1 AND day >= ?2 AND day <= ?3")
1089 .bind(&[s.own_meter.as_str().into(), since.as_str().into(), until.into()])?
1090 .first::<Own>(None)
1091 .await?
1092 .and_then(|o| o.total)
1093 .unwrap_or(0.0);
1094 let Some(per_unit) = derived_unit_cost(rate, cf_units, own_units) else { continue };
1095 let size = unit_size(s.unit.as_deref().unwrap_or("1"));
1096 let measured = per_unit * size * 1_000_000.0;
1097 let reason = format!(
1098 "Cloudflare billed ${:.4} per 1,000 of its units and counted {:.2} of them for each one g1t counted over the last {MEASURE_DAYS} days ({} against {})",
1099 rate * 1000.0,
1100 cf_units / own_units,
1101 crate::features::thousands(cf_units.round() as u64),
1102 crate::features::thousands(own_units.round() as u64)
1103 );
1104 if self.propose(&s.price_meter, measured, &reason, "reconciler").await?.is_some() {
1105 proposed += 1;
1106 }
1107 }
1108 Ok(proposed)
1109 }
1110
1111 /// Opens, updates and closes margin alerts, and emails staff about new
1112 /// ones (and open ones each week).
1113 async fn raise_alerts(&self, env: &Env, until: &str, drift: &[(Drift, String)]) -> Result<u32> {
1114 let settings = self.cost_settings().await?;
1115 let since = day_before(until, u64::from(settings.alert_days.max(1)) - 1);
1116 let days = self.margin_days(&since, until).await?;
1117 let mut conditions: Vec<(String, String, String, String)> = Vec::new();
1118 // Each product under the floor.
1119 let mut by: BTreeMap<String, Vec<(String, i64, i64)>> = BTreeMap::new();
1120 let mut all: BTreeMap<String, (i64, i64)> = BTreeMap::new();
1121 for d in &days {
1122 let overall = all.entry(d.day.clone()).or_default();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1123 // What g1t gave away (comped workspaces, free periods, the
1124 // trial and the pools) is a budget it chose to spend, watched on
1125 // its own (budget.rs): not part of whether what is sold pays.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1126 overall.0 += d.cash_micros;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1127 overall.1 += (d.cost() - d.given.total()).max(0);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1128 if !OVERHEAD.contains(&d.bucket.as_str()) && d.bucket != UNMAPPED {
1129 by.entry(d.bucket.clone()).or_default().push((d.day.clone(), d.value_micros, d.cost()));
1130 }
1131 }
1132 let floor = settings.margin_floor_percent;
1133 let n = settings.alert_days as usize;
1134 for (bucket, series) in &by {
1135 if let Some((from, worst)) = breach(series, floor, n, settings.min_daily_cost_micros) {
1136 conditions.push((
1137 "margin".into(),
1138 bucket.clone(),
1139 format!("{}: margin under {floor:.0}% for {n} days running, as low as {worst:.1}%.", costs::bucket_title(bucket)),
1140 from,
1141 ));
Margin alerts measure what is sold, and say dollars when a percentage would mislead1142 }
1143 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1144 let series: Vec<(String, i64, i64)> = all.into_iter().map(|(day, (revenue, cost))| (day, revenue, cost)).collect();
1145 if let Some((from, worst)) = breach(&series, floor, n, settings.min_daily_cost_micros) {
Margin alerts measure what is sold, and say dollars when a percentage would mislead1146 let tail = &series[series.len().saturating_sub(n)..];
1147 let (took, spent) = tail.iter().fold((0i64, 0i64), |(r, c), (_, revenue, cost)| (r + revenue, c + cost));
1148 conditions.push(("overall".into(), "g1t".into(), overall_detail(took, spent, n, floor, worst), from));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1149 }
1150 for (d, detail) in drift {
1151 let kind = if d.kind == DriftKind::Leak { "leak" } else { "drift" };
1152 conditions.push((kind.into(), format!("{}:{}", d.bucket, d.kind.as_str()), detail.clone(), until.to_owned()));
1153 }
1154 // Workspaces costing more than they pay.
1155 for (workspace, cost, revenue) in self.workspace_anomalies(until, &settings).await? {
1156 conditions.push((
1157 "workspace".into(),
1158 workspace.clone(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1159 format!(
1160 "{workspace} cost g1t {} on Cloudflare over {ANOMALY_DAYS} days, and its usage was priced at {}: its prices are below cost.",
1161 dollars(cost),
1162 dollars(revenue)
1163 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1164 day_before(until, ANOMALY_DAYS - 1),
1165 ));
1166 }
1167
1168 let open = self
1169 .db
1170 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL")
1171 .all()
1172 .await?
1173 .results::<AlertRow>()?;
1174 let now = now_ms();
1175 let stamp = rfc3339(now);
1176 let mut to_email: Vec<String> = Vec::new();
1177 let mut kept: BTreeSet<String> = BTreeSet::new();
1178 for (kind, subject, detail, from) in &conditions {
1179 match open.iter().find(|a| &a.kind == kind && &a.subject == subject) {
1180 Some(alert) => {
1181 kept.insert(alert.id.clone());
1182 self.db
1183 .prepare("UPDATE margin_alerts SET detail = ? WHERE id = ?")
1184 .bind(&[detail.as_str().into(), alert.id.as_str().into()])?
1185 .run()
1186 .await?;
1187 let stale = alert
1188 .emailed_at
1189 .as_deref()
1190 .and_then(g1t_contracts::time::parse_rfc3339)
1191 .is_none_or(|at| now.saturating_sub(at) >= REMIND_MS);
1192 if stale && kind != "workspace" {
1193 to_email.push(format!("Still open: {detail}"));
1194 kept.insert(format!("email:{}", alert.id));
1195 }
1196 }
1197 None => {
1198 let id = new_id("mal", now);
1199 self.db
1200 .prepare("INSERT INTO margin_alerts (id, kind, subject, detail, since, opened_at) VALUES (?, ?, ?, ?, ?, ?)")
1201 .bind(&[id.as_str().into(), kind.as_str().into(), subject.as_str().into(), detail.as_str().into(), from.as_str().into(), stamp.as_str().into()])?
1202 .run()
1203 .await?;
1204 kept.insert(id.clone());
Margin alerts measure what is sold, and say dollars when a percentage would mislead1205 // A workspace's is for Reach out, not the inbox.
1206 if kind != "workspace" {
1207 to_email.push(detail.clone());
1208 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1209 kept.insert(format!("email:{id}"));
1210 }
1211 }
1212 }
1213 for alert in &open {
1214 if !kept.contains(&alert.id) {
1215 self.db
1216 .prepare("UPDATE margin_alerts SET resolved_at = ? WHERE id = ?")
1217 .bind(&[stamp.as_str().into(), alert.id.as_str().into()])?
1218 .run()
1219 .await?;
1220 }
1221 }
1222 let to = env.var("COSTS_ALERT_EMAIL").map(|v| v.to_string()).unwrap_or_default();
1223 if !to_email.is_empty() && !to.trim().is_empty() {
1224 let subject = format!("[g1t costs] {} margin alert{}", to_email.len(), if to_email.len() == 1 { "" } else { "s" });
1225 match email_staff(env, to.trim(), &subject, &to_email).await {
1226 Ok(()) => {
1227 for marker in kept.iter().filter_map(|k| k.strip_prefix("email:")) {
1228 self.db
1229 .prepare("UPDATE margin_alerts SET emailed_at = ? WHERE id = ?")
1230 .bind(&[stamp.as_str().into(), marker.into()])?
1231 .run()
1232 .await?;
1233 }
1234 }
1235 Err(error) => worker::console_error!("could not email the margin alerts: {error}"),
1236 }
1237 }
1238 Ok(conditions.len() as u32)
1239 }
1240
1241 /// Workspaces costing g1t more than they pay over 30 days, not g1t's own.
Margin alerts measure what is sold, and say dollars when a percentage would mislead1242 /// Each day's cost shared out to comped workspaces.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1243 async fn workspace_anomalies(&self, until: &str, settings: &CostSettings) -> Result<Vec<(String, i64, i64)>> {
1244 #[derive(Deserialize)]
1245 struct Row {
1246 workspace: String,
1247 cost: Option<i64>,
1248 revenue: Option<i64>,
1249 }
1250 let rows = self
1251 .db
1252 .prepare(format!(
Margin alerts measure what is sold, and say dollars when a percentage would mislead1253 // Against what its usage was priced at, not the cash it
1254 // paid: a trial or a gift paying for usage is not a price
1255 // below cost.
The workspace cost alert compares only days that carry their value, not the days before it was kept1256 // Days from before value_micros was kept have none: only days
1257 // since the first one that does are compared.
Margin alerts measure what is sold, and say dollars when a percentage would mislead1258 "SELECT workspace, SUM(cost_micros) AS cost, SUM(value_micros) AS revenue FROM workspace_costs
The workspace cost alert compares only days that carry their value, not the days before it was kept1259 WHERE day >= ?1 AND day <= ?2 AND workspace NOT IN ({})
1260 AND day >= (SELECT MIN(day) FROM workspace_costs WHERE value_micros > 0)
1261 GROUP BY workspace",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1262 crate::sales::INTERNAL_SQL
1263 ))
1264 .bind(&[day_before(until, ANOMALY_DAYS - 1).into(), until.into()])?
1265 .all()
1266 .await?
1267 .results::<Row>()?;
1268 let rows: Vec<(String, i64, i64)> = rows.into_iter().map(|r| (r.workspace, r.cost.unwrap_or(0), r.revenue.unwrap_or(0))).collect();
1269 Ok(anomalies(&rows, settings.anomaly_factor, settings.anomaly_floor_micros))
1270 }
1271
1272 /// For Reach out: workspaces with an open cost-over-revenue alert,
1273 /// each with its detail and cost.
1274 pub(crate) async fn costing_more_than_they_pay(&self) -> Result<Vec<(String, String, i64)>> {
1275 let alerts = self
1276 .db
1277 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL AND kind = 'workspace' ORDER BY opened_at DESC LIMIT 50")
1278 .all()
1279 .await?
1280 .results::<AlertRow>()?;
1281 let mut out = Vec::new();
1282 for alert in alerts {
1283 #[derive(Deserialize)]
1284 struct Cost {
1285 cost: Option<i64>,
1286 }
1287 let cost = self
1288 .db
1289 .prepare("SELECT SUM(cost_micros) AS cost FROM workspace_costs WHERE workspace = ? AND day >= ?")
1290 .bind(&[alert.subject.as_str().into(), alert.since.as_str().into()])?
1291 .first::<Cost>(None)
1292 .await?
1293 .and_then(|c| c.cost)
1294 .unwrap_or(0);
1295 out.push((alert.subject, alert.detail, cost));
1296 }
1297 Ok(out)
1298 }
1299
1300 /// `admin_cost_alerts`: what sudo's banner says.
1301 pub(crate) async fn admin_cost_alerts(&self, _: AdminCostAlertsArgs) -> Result<Vec<MarginAlert>> {
1302 Ok(self
1303 .db
1304 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL ORDER BY opened_at DESC LIMIT 50")
1305 .all()
1306 .await?
1307 .results::<AlertRow>()?
1308 .into_iter()
1309 .map(MarginAlert::from)
1310 .collect())
1311 }
1312
1313 /// `admin_run_costs`: the daily run, now.
1314 pub(crate) async fn admin_run_costs(&self, env: &Env, a: AdminRunCostsArgs) -> Result<Outcome<CostsRun>> {
1315 let keeper = crate::keeper::Keeper::from_env(env);
1316 let run = self.costs_daily(env, &keeper).await?;
1317 if !a.by.is_empty() {
1318 self.audit(
1319 "costs",
1320 "costs_run",
1321 &format!("{} lines, {} days, {} proposals, {} alerts", run.lines, run.days, run.proposals, run.alerts),
1322 &a.by,
1323 )
1324 .await?;
1325 }
1326 Ok(Outcome::Ok(run))
1327 }
1328
1329 /// `admin_set_cost_mapping`.
1330 pub(crate) async fn admin_set_cost_mapping(&self, a: AdminSetCostMappingArgs) -> Result<Outcome<CostMapping>> {
1331 let product = costs::slug(&a.product);
1332 let meter = if a.meter.trim() == "*" { "*".to_owned() } else { costs::slug(&a.meter) };
1333 if product.is_empty() || meter.is_empty() {
1334 return Ok(Outcome::fail(FailureCode::Invalid, "Name Cloudflare's product and a meter (or * for all of it)."));
1335 }
1336 let now = rfc3339(now_ms());
1337 if a.remove {
1338 self.db
1339 .prepare("DELETE FROM cost_map WHERE product = ? AND meter = ?")
1340 .bind(&[product.as_str().into(), meter.as_str().into()])?
1341 .run()
1342 .await?;
1343 self.audit("costs", "cost_mapping_removed", &format!("{product}/{meter}"), &a.by).await?;
1344 return Ok(Outcome::Ok(CostMapping {
1345 product,
1346 meter,
1347 bucket: String::new(),
1348 price_meter: None,
1349 own_meter: None,
1350 scale_to_own: false,
1351 drift_percent: 0.0,
1352 note: String::new(),
1353 updated_at: now,
1354 updated_by: a.by,
1355 }));
1356 }
1357 let bucket = costs::slug(&a.bucket);
1358 if bucket.is_empty() {
1359 return Ok(Outcome::fail(FailureCode::Invalid, "Say which of g1t's products it is a cost of."));
1360 }
1361 let clean = |v: Option<String>| v.map(|v| v.trim().to_owned()).filter(|v| !v.is_empty());
1362 let (price_meter, own_meter) = (clean(a.price_meter), clean(a.own_meter));
1363 let drift = a.drift_percent.filter(|d| d.is_finite() && *d > 0.0).unwrap_or(10.0);
1364 self.db
1365 .prepare(
1366 "INSERT INTO cost_map (product, meter, bucket, price_meter, own_meter, scale_to_own, drift_percent, note, updated_at, updated_by)
1367 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
1368 ON CONFLICT (product, meter) DO UPDATE SET bucket = ?3, price_meter = ?4, own_meter = ?5, scale_to_own = ?6,
1369 drift_percent = ?7, note = ?8, updated_at = ?9, updated_by = ?10",
1370 )
1371 .bind(&[
1372 product.as_str().into(),
1373 meter.as_str().into(),
1374 bucket.as_str().into(),
1375 crate::optional(price_meter.as_deref()),
1376 crate::optional(own_meter.as_deref()),
1377 i32::from(a.scale_to_own).into(),
1378 drift.into(),
1379 a.note.trim().into(),
1380 now.as_str().into(),
1381 a.by.as_str().into(),
1382 ])?
1383 .run()
1384 .await?;
1385 self.audit("costs", "cost_mapping", &format!("{product}/{meter} → {bucket}"), &a.by).await?;
1386 Ok(Outcome::Ok(CostMapping {
1387 product,
1388 meter,
1389 bucket,
1390 price_meter,
1391 own_meter,
1392 scale_to_own: a.scale_to_own,
1393 drift_percent: drift,
1394 note: a.note.trim().to_owned(),
1395 updated_at: now,
1396 updated_by: a.by,
1397 }))
1398 }
1399
1400 /// `admin_costs`: the Costs & margin page.
1401 pub(crate) async fn admin_costs(&self, a: AdminCostsArgs, configured: bool) -> Result<CostsReport> {
1402 let until = rfc3339(now_ms())[..10].to_owned();
1403 let span = u64::from(a.days.unwrap_or(30).clamp(7, 90));
1404 let since = day_before(&until, span - 1);
1405 let days = self.margin_days(&since, &until).await?;
1406 let rules = self.rules().await?;
1407
1408 let mut products: BTreeMap<String, ProductMargin> = BTreeMap::new();
1409 let mut overall = OverallMargin::default();
1410 for d in &days {
1411 let p = products.entry(d.bucket.clone()).or_insert_with(|| ProductMargin {
1412 bucket: d.bucket.clone(),
1413 title: costs::bucket_title(&d.bucket),
1414 cost_source: if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) { "ledger" } else { "cloudflare" }.into(),
1415 overhead: OVERHEAD.contains(&d.bucket.as_str()),
1416 ..ProductMargin::default()
1417 });
1418 p.cf_cost_micros += d.cf_cost_micros;
1419 p.own_cost_micros += d.own_cost_micros;
1420 p.value_micros += d.value_micros;
1421 p.cost_micros += d.cost();
1422 overall.cost_micros += d.cost();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1423 overall.given_micros += d.given.total();
1424 if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) {
1425 overall.models_cost_micros += d.cost();
1426 } else {
1427 overall.cloudflare_cost_micros += d.cost();
1428 }
1429 overall.given_comped_micros += d.given.comped;
1430 overall.given_free_micros += d.given.free;
1431 overall.given_trial_micros += d.given.trial;
1432 overall.given_pool_micros += d.given.pool;
1433 let sold = (d.cost() - d.given.total()).max(0);
1434 if OVERHEAD.contains(&d.bucket.as_str()) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1435 overall.plans_micros += d.cash_micros;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1436 overall.running_cost_micros += sold;
1437 } else if d.bucket == UNMAPPED {
1438 overall.usage_micros += d.cash_micros;
1439 overall.unmapped_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1440 } else {
1441 overall.usage_micros += d.cash_micros;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1442 overall.usage_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1443 }
1444 }
1445 for p in products.values_mut() {
1446 p.margin_micros = p.value_micros - p.cost_micros;
1447 p.margin_percent = margin_percent(p.value_micros, p.cost_micros);
1448 }
1449 let revenue = overall.usage_micros + overall.plans_micros;
1450 overall.margin_micros = revenue - overall.cost_micros;
1451 overall.margin_percent = margin_percent(revenue, overall.cost_micros);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1452 let sold = (overall.cost_micros - overall.given_micros).max(0);
1453 overall.sold_margin_micros = revenue - sold;
1454 overall.sold_margin_percent = margin_percent(revenue, sold);
Costs: the plan's included usage counts as paid for the usage it covered, out of what plans leave for running g1t; the run button shows it is running with CSS alone (sudo ships no JavaScript)1455 // The plan's included usage was paid for by the plan's price: it is
1456 // money in for the usage it covered, and out of what the plans
1457 // leave for running g1t.
1458 #[derive(Deserialize)]
1459 struct Included {
1460 micros: Option<i64>,
1461 }
1462 overall.included_micros = self
1463 .db
1464 .prepare(format!(
1465 "SELECT SUM(COALESCE(credit_micros, 0)) AS micros FROM ledger
1466 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND workspace NOT IN ({})",
1467 crate::sales::INTERNAL_SQL
1468 ))
1469 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
1470 .first::<Included>(None)
1471 .await?
1472 .and_then(|r| r.micros)
1473 .unwrap_or(0);
1474 let usage_in = overall.usage_micros + overall.included_micros;
1475 overall.usage_margin_micros = usage_in - overall.usage_cost_micros;
1476 overall.usage_margin_percent = margin_percent(usage_in, overall.usage_cost_micros);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1477 let mut products: Vec<ProductMargin> = products.into_values().collect();
1478 products.sort_by_key(|p| std::cmp::Reverse(p.cost_micros.max(p.value_micros)));
1479
1480 #[derive(Deserialize)]
1481 struct DriftRow {
1482 bucket: String,
1483 kind: String,
1484 ours: f64,
1485 cloudflare: f64,
1486 delta_percent: Option<f64>,
1487 detail: String,
1488 found_at: String,
1489 }
1490 let drift = self
1491 .db
1492 .prepare("SELECT * FROM cost_drift ORDER BY kind, bucket")
1493 .all()
1494 .await?
1495 .results::<DriftRow>()?
1496 .into_iter()
1497 .map(|r| CostDrift {
1498 title: costs::bucket_title(&r.bucket),
1499 bucket: r.bucket,
1500 kind: r.kind,
1501 ours: r.ours,
1502 cloudflare: r.cloudflare,
1503 delta_percent: r.delta_percent,
1504 detail: r.detail,
1505 found_at: r.found_at,
1506 })
1507 .collect();
1508
1509 #[derive(Deserialize)]
1510 struct Top {
1511 workspace: String,
1512 cost: Option<i64>,
1513 revenue: Option<i64>,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1514 given: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1515 internal: i64,
1516 }
1517 let top_workspaces = self
1518 .db
1519 .prepare(format!(
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1520 "SELECT workspace, SUM(cost_micros) AS cost, SUM(revenue_micros) AS revenue, SUM(given_micros) AS given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1521 CASE WHEN workspace IN ({}) THEN 1 ELSE 0 END AS internal
1522 FROM workspace_costs WHERE day >= ?1 AND day <= ?2 GROUP BY workspace ORDER BY cost DESC LIMIT 15",
1523 crate::sales::INTERNAL_SQL
1524 ))
1525 .bind(&[since.as_str().into(), until.as_str().into()])?
1526 .all()
1527 .await?
1528 .results::<Top>()?
1529 .into_iter()
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1530 .map(|t| WorkspaceCost { workspace: t.workspace, cost_micros: t.cost.unwrap_or(0), revenue_micros: t.revenue.unwrap_or(0), given_micros: t.given.unwrap_or(0), internal: t.internal == 1 })
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1531 .collect();
1532
1533 #[derive(Deserialize)]
1534 struct Summary {
1535 source: String,
1536 product: String,
1537 meter: String,
1538 raw_name: String,
1539 unit: String,
1540 quantity: f64,
1541 cost_usd: f64,
1542 }
1543 let lines = self
1544 .db
1545 .prepare(
1546 "SELECT source, product, meter, MAX(raw_name) AS raw_name, MAX(unit) AS unit, SUM(quantity) AS quantity, SUM(cost_usd) AS cost_usd
1547 FROM cost_lines WHERE day >= ?1 AND day <= ?2 GROUP BY source, product, meter ORDER BY cost_usd DESC, product, meter LIMIT 200",
1548 )
1549 .bind(&[since.as_str().into(), until.as_str().into()])?
1550 .all()
1551 .await?
1552 .results::<Summary>()?
1553 .into_iter()
1554 .map(|l| CostLineSummary {
1555 bucket: costs::classify(&rules, &l.product, &l.meter).map(|r| r.bucket.clone()),
1556 product: l.product,
1557 meter: l.meter,
1558 raw_name: l.raw_name,
1559 unit: l.unit,
1560 source: l.source,
1561 quantity: l.quantity,
1562 cost_micros: micros(l.cost_usd),
1563 })
1564 .collect();
1565
1566 #[derive(Deserialize)]
1567 struct MapRow {
1568 product: String,
1569 meter: String,
1570 bucket: String,
1571 price_meter: Option<String>,
1572 own_meter: Option<String>,
1573 scale_to_own: i64,
1574 drift_percent: f64,
1575 note: String,
1576 updated_at: String,
1577 updated_by: String,
1578 }
1579 let mappings = self
1580 .db
1581 .prepare("SELECT * FROM cost_map ORDER BY product, meter")
1582 .all()
1583 .await?
1584 .results::<MapRow>()?
1585 .into_iter()
1586 .map(|m| CostMapping {
1587 product: m.product,
1588 meter: m.meter,
1589 bucket: m.bucket,
1590 price_meter: m.price_meter,
1591 own_meter: m.own_meter,
1592 scale_to_own: m.scale_to_own == 1,
1593 drift_percent: m.drift_percent,
1594 note: m.note,
1595 updated_at: m.updated_at,
1596 updated_by: m.updated_by,
1597 })
1598 .collect();
1599
1600 #[derive(Deserialize)]
1601 struct Fetched {
1602 at: Option<String>,
1603 }
1604 let fetched_at = self.db.prepare("SELECT MAX(fetched_at) AS at FROM cost_lines").first::<Fetched>(None).await?.and_then(|f| f.at);
1605
1606 Ok(CostsReport {
1607 configured,
1608 fetched_at,
1609 days: days
1610 .iter()
1611 .map(|d| CostDay {
1612 day: d.day.clone(),
1613 bucket: d.bucket.clone(),
1614 cf_cost_micros: d.cf_cost_micros,
1615 own_cost_micros: d.own_cost_micros,
1616 value_micros: d.value_micros,
1617 cash_micros: d.cash_micros,
1618 })
1619 .collect(),
1620 since,
1621 until,
1622 products,
1623 overall,
1624 drift,
1625 alerts: self.admin_cost_alerts(AdminCostAlertsArgs {}).await?,
1626 proposals: self.proposals().await?,
1627 versions: self.versions().await?,
1628 top_workspaces,
1629 lines,
1630 mappings,
1631 settings: self.cost_settings().await?,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1632 caps: self.spend_caps().await?,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1633 })
1634 }
1635}
1636
1637#[cfg(test)]
1638mod tests {
1639 use super::*;
1640
Margin alerts measure what is sold, and say dollars when a percentage would mislead1641 #[test]
Models' margin read -14%: usage nothing paid for is valued at price, not $01642 fn usage_nothing_paid_for_is_valued_at_price_and_paid_usage_at_what_was_paid() {
1643 // A free period: charged nothing, drawn from nothing.
1644 assert_eq!(usage_value(false, 1_000_000, 0, 20), 1_200_000);
1645 // Charged, or drawn from a trial: what was paid.
1646 assert_eq!(usage_value(false, 1_000_000, 1_200_000, 20), 1_200_000);
1647 assert_eq!(usage_value(false, 1_000_000, 900_000, 20), 900_000);
1648 // g1t's own: at price.
1649 assert_eq!(usage_value(true, 1_000_000, 0, 20), 1_200_000);
1650 // No cost, nothing paid: nothing.
1651 assert_eq!(usage_value(false, 0, 0, 20), 0);
1652 }
1653
1654 #[test]
Margin alerts measure what is sold, and say dollars when a percentage would mislead1655 fn the_overall_alert_says_dollars_while_little_comes_in() {
1656 let small = overall_detail(90_000, 7_500_000, 3, 10.0, -8239.7);
1657 assert!(small.contains("took in $0.09 against $7.50"), "{small}");
1658 assert!(!small.contains('%'), "{small}");
1659 let real = overall_detail(30_000_000, 40_000_000, 3, 10.0, -33.3);
1660 assert!(real.contains("as low as -33.3%"), "{real}");
1661 }
1662
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1663 fn rule(product: &str, meter: &str, bucket: &str, own: Option<&str>) -> Rule {
1664 Rule { product: product.into(), meter: meter.into(), bucket: bucket.into(), price_meter: None, own_meter: own.map(Into::into), drift_percent: 10.0 }
1665 }
1666
1667 fn rules() -> Vec<Rule> {
1668 vec![
1669 rule("containers", "*", "sandboxes", None),
1670 rule("workers", "*", "platform", None),
1671 rule("artifacts", "*", "git", Some("git_operations")),
1672 rule("artifacts", "events_", "git", Some("git_operations")),
1673 ]
1674 }
1675
1676 fn revenue_map() -> BTreeMap<String, String> {
1677 [("sandbox", "sandboxes"), ("git", "git"), ("plan", "platform")].iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()
1678 }
1679
1680 fn line(day: &str, source: &str, product: &str, meter: &str, quantity: f64, cost: f64) -> LineRow {
1681 LineRow { day: day.into(), source: source.into(), product: product.into(), meter: meter.into(), quantity, cost_usd: cost }
1682 }
1683
1684 fn usage(day: &str, workspace: &str, key: &str, value: i64, cash: i64, cost: i64) -> UsageRow {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1685 UsageRow { day: day.into(), workspace: workspace.into(), key: key.into(), value, cash, cost, given: Given::default() }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1686 }
1687
1688 #[test]
1689 fn a_day_puts_the_bill_g1ts_counts_and_charges_side_by_side() {
1690 let lines = vec![
1691 line("2026-10-15", SOURCE_BILLABLE, "containers", "container_memory", 1000.0, 2.00),
1692 line("2026-10-15", SOURCE_BILLABLE, "artifacts", "artifacts_operations", 30_000.0, 3.00),
1693 // Artifacts' own events: not used while the bill has a count.
1694 line("2026-10-15", SOURCE_ARTIFACTS, "artifacts", "events_pull", 29_000.0, 0.0),
1695 line("2026-10-15", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.50),
1696 line("2026-10-15", SOURCE_BILLABLE, "browser_rendering", "browser_hours", 2.0, 0.25),
1697 ];
1698 let own = vec![
1699 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "acme".into(), quantity: 7_500.0 },
1700 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "beta".into(), quantity: 2_500.0 },
1701 ];
1702 let usage = vec![
1703 usage("2026-10-15", "acme", "sandbox", 2_400_000, 1_000_000, 2_000_000),
1704 usage("2026-10-15", "beta", "sandbox", 1_200_000, 1_200_000, 1_000_000),
1705 usage("2026-10-15", "acme", "git", 600_000, 600_000, 500_000),
1706 usage("2026-10-15", "acme", "implement", 120_000, 120_000, 100_000),
1707 usage("2026-10-15", "beta", "plan", 20_000_000, 20_000_000, 0),
1708 ];
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1709 let (days, workspaces) = fold(&rules(), &revenue_map(), &lines, &own, &usage, &BTreeSet::new());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1710 let get = |bucket: &str| days.iter().find(|d| d.bucket == bucket).unwrap();
1711 let sandboxes = get("sandboxes");
1712 assert_eq!((sandboxes.cf_cost_micros, sandboxes.own_cost_micros, sandboxes.value_micros, sandboxes.cash_micros), (2_000_000, 3_000_000, 3_600_000, 2_200_000));
1713 let git = get("git");
1714 assert_eq!(git.cf_cost_micros, 3_000_000);
1715 assert_eq!((git.cf_quantity, git.own_quantity), (30_000.0, 10_000.0));
1716 assert_eq!(get("platform").value_micros, 20_000_000);
1717 // Not mapped: a leak until someone maps it.
1718 assert_eq!(get(UNMAPPED).cf_cost_micros, 250_000);
1719 // Models: no Cloudflare line, their cost is g1t's own.
1720 assert_eq!(get("models").cost(), 100_000);
1721 // Git's cost shared by g1t's own counts (Cloudflare gave none per
1722 // workspace here): three quarters to acme.
1723 let share = |ws: &str, bucket: &str| workspaces.iter().find(|w| w.workspace == ws && w.bucket == bucket).map(|w| (w.cost, w.revenue));
1724 assert_eq!(share("acme", "git"), Some((2_250_000, 600_000)));
1725 assert_eq!(share("beta", "git"), Some((750_000, 0)));
1726 // Every bucket's cost is shared out exactly.
1727 for d in &days {
1728 let shared: i64 = workspaces.iter().filter(|w| w.bucket == d.bucket).map(|w| w.cost).sum();
1729 assert_eq!(shared, d.cost(), "{}", d.bucket);
1730 }
1731 }
1732
1733 #[test]
1734 fn artifacts_events_count_when_the_bill_does_not() {
1735 let lines = vec![
1736 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_pull", 120.0, 0.0),
1737 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_push", 30.0, 0.0),
1738 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_ratelimited", 9.0, 0.0),
1739 ];
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1740 let (days, _) = fold(&rules(), &revenue_map(), &lines, &[], &[], &BTreeSet::new());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1741 assert_eq!(days[0].cf_quantity, 150.0);
1742 assert_eq!(days[0].cf_cost_micros, 0);
1743 }
1744
1745 #[test]
1746 fn month_end_meters_are_told_by_the_day_from_snapshots() {
1747 let snap = |day: &str, cost: i64, charge: i64| (day.to_string(), "acme".to_string(), "git".to_string(), cost, charge);
1748 let rows = pending_deltas(&[snap("2026-10-30", 100, 120), snap("2026-10-31", 250, 300), snap("2026-11-01", 40, 48), snap("2026-11-02", 40, 48)]);
1749 assert_eq!(
1750 rows.iter().map(|r| (r.day.as_str(), r.cost, r.value)).collect::<Vec<_>>(),
1751 vec![("2026-10-30", 100, 120), ("2026-10-31", 150, 180), ("2026-11-01", 40, 48)]
1752 );
1753 }
1754
1755 #[test]
1756 fn a_plan_payment_is_spread_over_the_month_it_pays_for() {
1757 let days = spread("2026-10-01T00:00:00.000Z", 20_000_000, 30);
1758 assert_eq!(days.len(), 30);
1759 assert_eq!(days[0], ("2026-10-01".to_string(), 666_667));
1760 assert_eq!(days[29], ("2026-10-30".to_string(), 666_666));
1761 assert_eq!(days.iter().map(|d| d.1).sum::<i64>(), 20_000_000);
1762 assert!(spread("2026-10-01", 0, 30).is_empty());
1763 assert_eq!(dollars(17_024_000), "$17.02");
1764 assert_eq!(dollars(-27_668_620), "-$27.67");
1765 assert_eq!(dollars(63_000), "$0.063");
1766 }
1767
1768 #[test]
1769 fn margins_and_deltas() {
1770 assert_eq!(margin_percent(1_200_000, 1_000_000).map(|m| (m * 100.0).round() / 100.0), Some(16.67));
1771 assert_eq!(margin_percent(0, 5), None);
1772 assert_eq!(delta_percent(110.0, 100.0), Some(10.0));
1773 assert_eq!(delta_percent(1.0, 0.0), None);
1774 }
1775
1776 fn day(bucket: &str, cf: i64, own: i64, value: i64, cfq: f64, ownq: f64) -> ProductDay {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1777 ProductDay { day: "2026-10-15".into(), bucket: bucket.into(), cf_cost_micros: cf, own_cost_micros: own, value_micros: value, cash_micros: value, cf_quantity: cfq, own_quantity: ownq, given: Given::default() }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1778 }
1779
1780 #[test]
1781 fn counts_more_than_the_threshold_apart_are_drift() {
1782 // Cloudflare counted 30,000 operations where g1t counted 10,000:
1783 // binding reads, perhaps. -66.7%.
1784 let drift = drifts("git", &[day("git", 3_000_000, 1_500_000, 1_800_000, 30_000.0, 10_000.0)], 10.0, true, 100_000);
1785 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Count, DriftKind::Cost]);
1786 assert!((drift[0].delta_percent.unwrap() + 66.666).abs() < 0.01);
1787 // 9% apart: within 10%.
1788 assert!(drifts("git", &[day("git", 1_000_000, 1_000_000, 1_200_000, 10_000.0, 10_900.0)], 10.0, true, 100_000).is_empty());
1789 // Uncounted products have no count drift.
1790 assert!(drifts("sandboxes", &[day("sandboxes", 1_000_000, 1_050_000, 1_200_000, 5.0, 0.0)], 10.0, false, 100_000).is_empty());
1791 }
1792
1793 #[test]
1794 fn cost_with_no_revenue_is_a_leak_but_not_for_running_g1t() {
1795 let leak = drifts("actions_cache", &[day("actions_cache", 400_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
1796 assert_eq!(leak.len(), 1);
1797 assert_eq!(leak[0].kind, DriftKind::Leak);
1798 assert!(drifts("platform", &[day("platform", 5_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
1799 // Pennies say nothing.
1800 assert!(drifts("actions_cache", &[day("actions_cache", 50_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
1801 assert!(drifts(UNMAPPED, &[day(UNMAPPED, 250_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000)[0].kind == DriftKind::Leak);
1802 }
1803
1804 #[test]
1805 fn a_margin_alert_needs_n_days_in_a_row_under_the_floor() {
1806 let s = |d: &str, revenue: i64, cost: i64| (d.to_string(), revenue, cost);
1807 // 5%, 0%, -20%: three days under 10%.
1808 let series = vec![s("10-13", 1_200_000, 1_000_000), s("10-14", 1_050_000, 1_000_000), s("10-15", 1_000_000, 1_000_000), s("10-16", 1_000_000, 1_200_000)];
1809 let (from, worst) = breach(&series, 10.0, 3, 100_000).unwrap();
1810 assert_eq!(from, "10-14");
1811 assert!((worst + 20.0).abs() < 1e-9);
1812 // A good day in the window clears it.
1813 let mended = vec![s("10-14", 1_050_000, 1_000_000), s("10-15", 1_300_000, 1_000_000), s("10-16", 1_000_000, 1_200_000)];
1814 assert!(breach(&mended, 10.0, 3, 100_000).is_none());
1815 // Cost with no revenue at all is the worst margin there is.
1816 assert_eq!(breach(&[s("10-16", 0, 500_000)], 10.0, 1, 100_000).unwrap().1, -100.0);
1817 // Too little cost to judge.
1818 assert!(breach(&[s("10-16", 0, 5_000)], 10.0, 1, 100_000).is_none());
1819 assert!(breach(&series, 10.0, 9, 100_000).is_none());
1820 }
1821
1822 #[test]
1823 fn shared_costs_add_up_to_the_bill() {
1824 let w = |k: &str, v: f64| (k.to_string(), v);
1825 assert_eq!(attribute(100, &[w("a", 1.0), w("b", 1.0), w("c", 1.0)]), vec![("a".into(), 34), ("b".into(), 33), ("c".into(), 33)]);
1826 assert_eq!(attribute(10, &[w("a", 3.0), w("b", 1.0), w("a", 0.0)]), vec![("a".into(), 8), ("b".into(), 2)]);
1827 assert!(attribute(10, &[w("a", 0.0)]).is_empty());
1828 assert!(attribute(0, &[w("a", 1.0)]).is_empty());
1829 }
1830
1831 #[test]
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift1832 fn counts_are_compared_from_the_day_g1t_started_counting() {
1833 let on = |day: &str, cf: f64, own: f64| ProductDay { day: day.into(), bucket: "git".into(), cf_quantity: cf, own_quantity: own, ..ProductDay::default() };
1834 // Five days of Cloudflare's count before g1t's meter, then two that match.
1835 let days = vec![on("2026-10-01", 500.0, 0.0), on("2026-10-05", 300.0, 0.0), on("2026-10-06", 210.0, 231.0), on("2026-10-07", 450.0, 458.0)];
1836 assert!(drifts("git", &days, 10.0, true, 0).iter().all(|d| d.kind != DriftKind::Count));
1837 // A real gap on the days both counted still shows.
1838 let days = vec![on("2026-10-01", 500.0, 0.0), on("2026-10-06", 400.0, 231.0), on("2026-10-07", 600.0, 300.0)];
1839 let found = drifts("git", &days, 10.0, true, 0);
1840 let count = found.iter().find(|d| d.kind == DriftKind::Count).unwrap();
1841 assert_eq!((count.ours, count.cloudflare), (531.0, 1000.0));
1842 // A meter that never counted is compared over every day.
1843 let days = vec![on("2026-10-06", 400.0, 0.0)];
1844 assert!(drifts("git", &days, 10.0, true, 0).iter().any(|d| d.kind == DriftKind::Count));
1845 }
1846
1847 #[test]
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1848 fn what_g1t_gives_away_is_kept_apart_from_what_it_sells() {
1849 let map = BTreeMap::new();
1850 // A comped workspace (all of it given), one in its trial (half paid
1851 // by the trial) and one paying in cash, all on models.
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1852 let comped = usage("2026-10-15", "flagon", "agent", 1_200_000, 0, 1_000_000);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1853 let mut trial = usage("2026-10-15", "acme", "agent", 1_200_000, 600_000, 1_000_000);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1854 trial.given = Given { trial: 600_000, ..Given::default() };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1855 let paying = usage("2026-10-15", "beta", "agent", 1_200_000, 1_200_000, 1_000_000);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1856 // Nothing priced that day: free use.
1857 let free = usage("2026-10-15", "gamma", "agent", 0, 0, 1_000_000);
1858 let internal = BTreeSet::from(["flagon".to_string()]);
1859 let (days, workspaces) = fold(&[], &map, &[], &[], &[comped, trial, paying, free], &internal);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1860 let models = days.iter().find(|d| d.bucket == "models").unwrap();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1861 assert_eq!(models.cost(), 4_000_000);
1862 assert_eq!(models.given, Given { comped: 1_000_000, free: 1_000_000, trial: 500_000, pool: 0 });
1863 let given = |w: &str| workspaces.iter().find(|x| x.workspace == w).unwrap().given.total();
1864 assert_eq!((given("flagon"), given("acme"), given("beta"), given("gamma")), (1_000_000, 500_000, 0, 1_000_000));
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1865 }
1866
1867 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1868 fn a_workspace_that_costs_more_than_it_pays_is_flagged() {
1869 let rows = vec![("acme".to_string(), 5_000_000, 1_000_000), ("beta".to_string(), 900_000, 0), ("gamma".to_string(), 2_000_000, 3_000_000)];
1870 let found = anomalies(&rows, 1.0, 1_000_000);
1871 assert_eq!(found, vec![("acme".to_string(), 5_000_000, 1_000_000)]);
1872 // At twice its revenue as the threshold, $5 against $3 is fine.
1873 assert!(anomalies(&[("acme".to_string(), 5_000_000, 3_000_000)], 2.0, 1_000_000).is_empty());
1874 }
1875
1876 #[test]
1877 fn a_git_operation_costs_what_cloudflare_counts_for_it() {
1878 // $0.15 per 1,000 of Cloudflare's operations, on the charged days.
1879 let rate = billed_rate(&[(10_000.0, 0.0), (20_000.0, 3.0), (30_000.0, 4.5), (5_000.0, 0.75)]).unwrap();
1880 assert!((rate - 0.000_15).abs() < 1e-12);
1881 // Cloudflare counted 3 for every 1 g1t did: binding reads count.
1882 let per_op = derived_unit_cost(rate, 300_000.0, 100_000.0).unwrap();
1883 let per_thousand_micros = per_op * unit_size("1,000 operations") * 1e6;
1884 assert!((per_thousand_micros - 450_000.0).abs() < 1e-6, "{per_thousand_micros}");
1885 // Too few of g1t's units to say.
1886 assert!(derived_unit_cost(rate, 3_000.0, 500.0).is_none());
1887 assert!(billed_rate(&[(10_000.0, 0.0)]).is_none());
1888 assert_eq!(unit_size("million requests"), 1e6);
1889 assert_eq!(unit_size("second"), 1.0);
1890 }
1891}