39 lines · 1.5 KB
import type { CounterKind, Totals } from "./counters-do";

type Row = { key: string; a: number; b: number; c: number };

type CounterStub = {
  add(kind: CounterKind, scope: string, key: string, delta: Totals, meta: string | null): Promise<Totals>;
};

/**
 * Adds what each flushed row gained since its last flush (its `base_*` columns) to a platform total in another
 * Counters object, per key. Best effort: a lost forward only skews a platform safety net, never a bill.
 */
export async function forwardFlushed(
  sql: SqlStorage,
  namespace: DurableObjectNamespace,
  target: { kind: CounterKind; scope: string },
  rows: Row[],
): Promise<void> {
  const deltas = rows.flatMap((row) => {
    const base = sql
      .exec<{ base_a: number | null; base_b: number | null; base_c: number | null }>(
        "SELECT base_a, base_b, base_c FROM counters WHERE key = ?",
        row.key,
      )
      .toArray()[0];
    const delta: Totals = [row.a - (base?.base_a ?? 0), row.b - (base?.base_b ?? 0), row.c - (base?.base_c ?? 0)];
    return delta.some((value) => value !== 0) ? [{ key: row.key, delta }] : [];
  });
  if (deltas.length === 0) return;
  const stub = namespace.get(namespace.idFromName(`${target.kind}:${target.scope}`)) as unknown as CounterStub;
  await Promise.all(
    deltas.map(({ key, delta }) =>
      stub
        .add(target.kind, target.scope, key, delta, null)
        .catch((error: unknown) => console.warn("Counter forward failed", target, key, error)),
    ),
  );
}