import { first } from "../db";
import { backfillOnce, backfillRepos } from "../backfill";
import { locatedScopes } from "../scoped-db/locators";
import { accountDb, repoDb, stmt } from "../scoped-db";
import type { ScopedStatement } from "../scoped-db";
import { noteScratchAccount } from "./directory";
import { gaugeStatement, resolveAccount } from "./record";
import type { MeterId } from "~/lib/meters";
/**
* Storage gauges without a cron over every repository: each repository or site scope (RepoState) computes what
* it stores per billed account and writes it to that account's `scope_gauges`; the account's gauge for a meter
* is the sum over its scopes. A scope recomputes after a debounce when its storage tables change (RepoState
* alarm task `gauges`), and an account re-sums when its `scope_gauges` change (AccountState task `scope-gauges`).
*/
export const SCOPE_METERS: MeterId[] = [
"storage.ci_cache",
"storage.artifacts",
"storage.r2",
"storage.sites",
"domains.custom",
];
type GaugeRow = { accountId: string; meter: MeterId; value: number };
async function scopeOwner(scopeId: string): Promise<string | null> {
if (scopeId.startsWith("site:")) return null;
return (await first<{ owner_id: string }>("SELECT owner_id FROM repos WHERE id = ?", scopeId))?.owner_id ?? null;
}
/** What this scope stores per billed account (claimed scratch ids resolved to their claimer). */
export async function computeScopeGauges(scopeId: string): Promise<GaugeRow[]> {
const db = repoDb(scopeId);
const raw: GaugeRow[] = [];
const owner = await scopeOwner(scopeId);
if (owner) {
const [cache, artifacts, stored] = await Promise.all([
// Cache objects are content-addressed within the repository: bill each stored object once.
db.first<{ value: number | null }>(
"SELECT SUM(size) AS value FROM (SELECT DISTINCT r2_key, size FROM action_caches WHERE size > 0)",