import { DurableObject } from "cloudflare:workers";
import { backoffMs, flushIsolating, ISOLATE_AFTER } from "../do-flush";
import { accountDb } from "../scoped-db";
import { eventStatements, periodOf } from "./record";
import type { UsageInput } from "./record";
const FLUSH_DELAY_MS = 5_000;
const RETRY_DELAY_MS = 30_000;
/** D1 batches are one transaction; this keeps each well under the statement limits. */
const FLUSH_BATCH = 100;
/** Flushed keys are remembered this long so a retried request is still recognised as a duplicate. */
const LEDGER_MS = 24 * 60 * 60 * 1000;
type Pending = { key: string; input: string; at: number };
/**
* One per billed account: buffers high-volume usage events (git operations, downloads) and lands them in
* the account's AccountState as one batched transaction every few seconds instead of one write per request. D1 stays the billing
* source of truth: events keep their idempotency keys and are inserted with INSERT OR IGNORE, so a flush
* that is retried after a partial failure never double counts. Quota checks read D1 and therefore see
* buffered usage up to FLUSH_DELAY_MS late; anything that must be exact uses `recordUsage` directly.
*/
export class MeterBuffer extends DurableObject<Env> {
private ready = false;
private init(): void {
if (this.ready) return;
this.ctx.storage.sql.exec(`CREATE TABLE IF NOT EXISTS events (
key TEXT PRIMARY KEY,
input TEXT NOT NULL,
at INTEGER NOT NULL,
flushed_at INTEGER
)`);
this.ctx.storage.sql.exec("CREATE INDEX IF NOT EXISTS events_pending ON events(flushed_at, at)");
this.ctx.storage.sql.exec(`CREATE TABLE IF NOT EXISTS dead_letters (
key TEXT PRIMARY KEY,
input TEXT NOT NULL,
error TEXT NOT NULL,
at INTEGER NOT NULL
)`);