Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
76cba4c
perf(database): index month speech usage, held transcript credit and …
xxxxxxxxxxxxx Nov 13, 2026
20a5118
perf(web): page the Infra billing ledger by cursor without counting it
xxxxxxxxxxxxx Nov 13, 2026
2fb3c00
perf(web): read stored video once per request and load only what each…
xxxxxxxxxxxxx Nov 13, 2026
7b33c29
perf(web): page the storage cron and auto top-up pass and stop before…
xxxxxxxxxxxxx Nov 13, 2026
43071fa
Merge remote-tracking branch 'origin/main' into perf/infra-billing-reads
xxxxxxxxxxxxx Nov 13, 2026
acb2776
perf(web): page the storage cron on the account id alone so a page ne…
xxxxxxxxxxxxx Nov 13, 2026
162da86
perf(database): index ledger rows by reference so claims can be count…
xxxxxxxxxxxxx Nov 13, 2026
416ecba
Merge remote-tracking branch 'origin/main' into perf/infra-billing-reads
xxxxxxxxxxxxx Nov 13, 2026
d70f895
chore(database): renumber the billing read indexes migration to 0058
xxxxxxxxxxxxx Nov 13, 2026
878152f
fix(web): continue a cut-off storage sweep for the same day from the …
xxxxxxxxxxxxx Nov 13, 2026
cc4ceee
refactor(web): remove the unused ledger page window
xxxxxxxxxxxxx Nov 13, 2026
2216ac3
perf(web): look up the last auto top-up by account and ledger type
xxxxxxxxxxxxx Nov 13, 2026
ba5a834
fix(web): keep a recorded unpaid storage day final when finishing a p…
xxxxxxxxxxxxx Nov 13, 2026
2612a10
fix(web): finish yesterday's storage sweep before today's on each sch…
xxxxxxxxxxxxx Nov 13, 2026
b178cba
feat(web): let the stored video read count only videos created before…
xxxxxxxxxxxxx Nov 13, 2026
f50866e
fix(web): charge a finished storage day only for what existed on that…
xxxxxxxxxxxxx Nov 13, 2026
fd7a441
chore(database): number the billing read indexes migration 0056
xxxxxxxxxxxxx Nov 13, 2026
e0746ea
feat(web): record storage day visits and keep them out of snapshot re…
xxxxxxxxxxxxx Nov 13, 2026
42b7b13
fix(web): finish a past storage day only for accounts the sweep never…
xxxxxxxxxxxxx Nov 13, 2026
a1163cf
test(web): charge a video ready after a day's last storage run from t…
xxxxxxxxxxxxx Nov 13, 2026
06ca97f
Merge branch 'main' into perf/infra-billing-reads
richiemcilroy Nov 13, 2026
aaf0035
Merge remote-tracking branch 'origin/main' into perf/infra-billing-reads
xxxxxxxxxxxxx Nov 13, 2026
237784a
fix(web): read the latest storage charge by the day it bills
xxxxxxxxxxxxx Nov 13, 2026
c606af7
fix(web): offer auto top-ups to the least recently tried due account …
xxxxxxxxxxxxx Nov 13, 2026
240d9a1
Merge remote-tracking branch 'origin/main' into perf/infra-billing-reads
xxxxxxxxxxxxx Nov 13, 2026
91a8120
Merge remote-tracking branch 'origin/main' into perf/infra-billing-reads
xxxxxxxxxxxxx Nov 13, 2026
5f97bf0
Merge remote-tracking branch 'origin/main' into perf/infra-billing-reads
xxxxxxxxxxxxx Nov 13, 2026
0850363
Merge remote-tracking branch 'origin/main' into perf/infra-billing-reads
xxxxxxxxxxxxx Nov 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions apps/web/__tests__/unit/developer-api-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ import {
INFRA_RESOLUTION_TIERS,
INFRA_STORAGE_DOLLARS_PER_1000_MINUTES_PER_MONTH,
} from "@/lib/infra-pricing";
import { getAppStorage } from "@/lib/infra-storage";

type Method = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS";

Expand Down Expand Up @@ -157,6 +158,11 @@ const rateLimit = (response: Response) => ({
retryAfter: response.headers.get("Retry-After"),
});

const billingOf = async (billing: Record<string, unknown>) => ({
...billing,
stored: (await getAppStorage("app-1")).stored,
});

beforeEach(() => {
vi.clearAllMocks();
mocks.keyRows = [{ appId: "app-1", environment: "development" }];
Expand All @@ -183,7 +189,9 @@ beforeEach(() => {
},
];
mocks.selected = [];
mocks.billing.mockResolvedValue({ standing: { state: "active" } });
mocks.billing.mockImplementation(() =>
billingOf({ standing: { state: "active" } }),
);
mocks.createVideo.mockResolvedValue({
status: 200,
body: { videoId: "video-1" },
Expand Down Expand Up @@ -898,7 +906,9 @@ describe("usage", () => {
});

it("returns the fields the OpenAPI description requires", async () => {
mocks.billing.mockResolvedValue({ standing: {}, upload: {}, storage: {} });
mocks.billing.mockImplementation(() =>
billingOf({ standing: {}, upload: {}, storage: {} }),
);
const schema = developerOpenApiSpec(ORIGIN).components.schemas.Usage;

const data = await usage();
Expand Down
126 changes: 107 additions & 19 deletions apps/web/__tests__/unit/developer-cron-storage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,96 @@ import { beforeEach, describe, expect, it, vi } from "vitest";

const mocks = vi.hoisted(() => ({
selects: [] as Record<string, unknown>[][],
page: [] as Record<string, unknown>[],
accountsTable: { id: "id", appId: "appId" },
appsTable: { id: "id", deletedAt: "deletedAt" },
ledgerTable: {
accountId: "accountId",
referenceId: "referenceId",
referenceType: "referenceType",
},
snapshotsTable: { appId: "appId", snapshotDate: "snapshotDate" },
yesterdayBilled: null as Record<string, unknown>[] | null,
markers: [] as unknown[],
getStorageUsageByApp: vi.fn(),
chargeStorageDay: vi.fn(),
latestStorageCharge: vi.fn(),
latestStorageCharges: vi.fn(),
runDueAutoTopUps: vi.fn(),
sendInfraOutOfCreditEmail: vi.fn(),
}));

vi.mock("@cap/database", () => ({
db: () => {
let table: unknown;
const dateOf = (condition: unknown) => {
const match = JSON.stringify(condition).match(
/\{"eq":\["referenceId","(\d{4}-\d{2}-\d{2})"\]\}/,
);
return match?.[1] ?? null;
};
const read = (condition: unknown) => {
if (table === mocks.appsTable) {
return mocks.page.map((account) => ({ id: account.appId }));
}
if (table === mocks.accountsTable) {
mocks.page = mocks.selects[0] ?? [];
return mocks.page;
}
if (table === mocks.ledgerTable) {
if (dateOf(condition) === new Date().toISOString().slice(0, 10)) {
return mocks.selects[1] ?? [];
}
return (
mocks.yesterdayBilled ??
mocks.page.map((account) => ({ accountId: account.id }))
);
}
return [];
};
const chain = {
select: () => chain,
from: () => chain,
innerJoin: () => chain,
where: async () => mocks.selects.shift() ?? [],
from: (next: unknown) => {
table = next;
return chain;
},
where: (condition: unknown) => {
const rows = Promise.resolve(read(condition));
return Object.assign(rows, {
orderBy: () => ({ limit: () => rows }),
});
},
};
return chain;
},
}));
vi.mock("@cap/database/schema", () => ({
developerApps: { id: "id", deletedAt: "deletedAt" },
developerCreditAccounts: { id: "id", appId: "appId" },
developerCreditTransactions: {
accountId: "accountId",
referenceId: "referenceId",
referenceType: "referenceType",
},
developerApps: mocks.appsTable,
developerCreditAccounts: mocks.accountsTable,
developerCreditTransactions: mocks.ledgerTable,
developerDailyStorageSnapshots: mocks.snapshotsTable,
}));
vi.mock("drizzle-orm", () => ({
and: (...args: unknown[]) => args,
asc: (a: unknown) => ({ asc: a }),
gt: (a: unknown, b: unknown) => ({ gt: [a, b] }),
eq: (a: unknown, b: unknown) => ({ eq: [a, b] }),
inArray: (a: unknown, b: unknown) => ({ inArray: [a, b] }),
isNull: (a: unknown) => ({ isNull: a }),
isNotNull: (a: unknown) => ({ isNotNull: a }),
}));
vi.mock("@/lib/infra-storage", () => ({
getStorageUsageByApp: mocks.getStorageUsageByApp,
}));
vi.mock("@/lib/infra-storage-billing", () => ({
chargeStorageDay: mocks.chargeStorageDay,
recordStorageDayVisits: async ({ appIds }: { appIds: string[] }) => {
mocks.markers.push(...appIds);
},
}));
vi.mock("@/lib/infra-standing", () => ({
INFRA_GRACE_DAYS: 7,
STORAGE_DAY_REFERENCE_TYPE: "storage_day",
latestStorageCharge: mocks.latestStorageCharge,
latestStorageCharges: mocks.latestStorageCharges,
}));
vi.mock("@/lib/infra-auto-top-up", () => ({
runDueAutoTopUps: mocks.runDueAutoTopUps,
Expand Down Expand Up @@ -90,17 +137,20 @@ const authorized = () => run(`Bearer ${CRON_SECRET}`);
beforeEach(() => {
process.env.CRON_SECRET = CRON_SECRET;
mocks.selects = [];
mocks.page = [];
mocks.yesterdayBilled = null;
mocks.markers = [];
for (const mock of [
mocks.getStorageUsageByApp,
mocks.chargeStorageDay,
mocks.latestStorageCharge,
mocks.latestStorageCharges,
mocks.runDueAutoTopUps,
mocks.sendInfraOutOfCreditEmail,
]) {
mock.mockReset();
}
mocks.runDueAutoTopUps.mockResolvedValue(NO_TOP_UPS);
mocks.latestStorageCharge.mockResolvedValue(null);
mocks.latestStorageCharges.mockResolvedValue(new Map());
mocks.chargeStorageDay.mockResolvedValue(billed());
});

Expand Down Expand Up @@ -191,21 +241,59 @@ describe("developer-storage cron job", () => {

expect(body.appsProcessed).toBe(0);
expect(mocks.chargeStorageDay).not.toHaveBeenCalled();
expect(mocks.markers).toEqual(["app-1"]);
});

it("looks up the last charge of every idle account in one read", async () => {
mocks.selects = [
[
{ id: "account-1", appId: "app-1" },
{ id: "account-2", appId: "app-2" },
{ id: "account-3", appId: "app-3" },
],
[],
];
mocks.getStorageUsageByApp.mockResolvedValue(
new Map([
["app-1", usage(0)],
["app-2", usage(28)],
["app-3", usage(0)],
]),
);

const { body } = await authorized();

expect(body).toMatchObject({ complete: true, appsProcessed: 1 });
expect(mocks.latestStorageCharges).toHaveBeenCalledTimes(1);
expect(mocks.latestStorageCharges).toHaveBeenCalledWith(expect.anything(), [
"account-1",
"account-3",
]);
});

it("still settles an app that stores nothing now but is short from before", async () => {
mocks.selects = [[{ id: "account-1", appId: "app-1" }], []];
mocks.getStorageUsageByApp.mockResolvedValue(
new Map([["app-1", usage(0)]]),
);
mocks.latestStorageCharge.mockResolvedValue({
createdAt: new Date(),
owedMicroCredits: 28,
shortSince: new Date(),
});
mocks.latestStorageCharges.mockResolvedValue(
new Map([
[
"account-1",
{
createdAt: new Date(),
owedMicroCredits: 28,
shortSince: new Date(),
},
],
]),
);

await authorized();

expect(mocks.latestStorageCharges).toHaveBeenCalledWith(expect.anything(), [
"account-1",
]);
expect(mocks.chargeStorageDay).toHaveBeenCalledTimes(1);
});

Expand Down
6 changes: 4 additions & 2 deletions apps/web/__tests__/unit/developer-usage-counts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ vi.mock("@/lib/infra-standing", async (original) => ({

import { app } from "@/app/api/developer/v1/[...route]/usage";
import { storageUsage } from "@/lib/developer-pricing";
import { getAppStorage } from "@/lib/infra-storage";

const usage = async () => {
const response = await app.request("/", {
Expand All @@ -99,17 +100,18 @@ const usage = async () => {
beforeEach(() => {
mocks.balanceMicroCredits = 2_999_901;
mocks.videos = [];
mocks.billing.mockResolvedValue({
mocks.billing.mockImplementation(async (appId: string) => ({
standing: { state: "active", pausesAt: null },
usage: storageUsage([]),
stored: (await getAppStorage(appId)).stored,
admission: {
allowed: true,
reason: null,
requiredMicroCredits: 1,
shortfallMicroCredits: 0,
message: null,
},
});
}));
});

describe("GET /usage totals", () => {
Expand Down
7 changes: 3 additions & 4 deletions apps/web/__tests__/unit/developer-usage-route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,15 @@ vi.mock("@/lib/developer-key-hash", () => ({
hashKey: async (key: string) => `hash:${key}`,
}));
vi.mock("@/lib/infra-standing", () => ({
getInfraBilling: async () => ({}),
getInfraBilling: async () => ({
stored: { videoCount: 2, totalMinutes: 12.5 },
}),
infraBillingJson: () => ({
standing: { state: "active", pausesAt: null },
upload: { allowed: true },
}),
infraStorageJson: () => ({ dailyMicroCredits: 0 }),
}));
vi.mock("@/lib/infra-storage", () => ({
getAppStorageUsage: async () => ({ videoCount: 2, totalMinutes: 12.5 }),
}));
vi.mock("@/lib/developer-transcript-usage", () => ({
getSpeechUsage: mocks.speech,
}));
Expand Down
21 changes: 20 additions & 1 deletion apps/web/__tests__/unit/developer-video-upload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ vi.mock("@/lib/infra-storage", async () => {
const { billableVideo, storageUsage } = await import(
"@/lib/developer-pricing"
);
return {
const storage = {
getAppStorageUsage: async (
appId: string,
newVideo?: Parameters<typeof storageUsage>[0][number]["video"],
Expand Down Expand Up @@ -129,6 +129,25 @@ vi.mock("@/lib/infra-storage", async () => {
return storageUsage(entries);
},
};
return {
...storage,
getAppStorage: async (
...[appId, newVideo, executor, options]: Parameters<
typeof storage.getAppStorageUsage
>
) => ({
stored: await storage.getAppStorageUsage(
appId,
newVideo,
executor,
options,
),
counted: await storage.getAppStorageUsage(appId, newVideo, executor, {
...options,
includeProcessing: true,
}),
}),
};
});

vi.mock("@/lib/developer-transcript-storage", () => ({
Expand Down
Loading
Loading