From ff4b8212dd39474bb85edd6134fda977c933c6c8 Mon Sep 17 00:00:00 2001 From: felixvippp-ai Date: Wed, 24 Jun 2026 11:56:18 -0400 Subject: [PATCH 1/5] perf: use usage index totals in metrics --- src/routes/metrics.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/routes/metrics.ts b/src/routes/metrics.ts index e8f2dd4..ebd6b31 100644 --- a/src/routes/metrics.ts +++ b/src/routes/metrics.ts @@ -1,5 +1,11 @@ import { Router, type Response } from "express"; -import { apiKeyStore, pauseState, servicesStore, usageStore } from "../store/state.js"; +import { + apiKeyStore, + getUsageTotalRequests, + pauseState, + servicesStore, + usageByAgent, +} from "../store/state.js"; /** * Builds operational metrics and aggregate stats routes. @@ -8,8 +14,7 @@ export function createMetricsRouter(): Router { const router = Router(); router.get("/api/v1/metrics", (_req, res: Response) => { - let totalRequests = 0; - for (const v of usageStore.values()) totalRequests += v; + const totalRequests = getUsageTotalRequests(); const lines = [ "# HELP agentpay_services_total Number of registered services.", "# TYPE agentpay_services_total gauge", @@ -29,17 +34,11 @@ export function createMetricsRouter(): Router { }); router.get("/api/v1/stats", (_req, res: Response) => { - let totalRequests = 0; - const agents = new Set(); - for (const [key, total] of usageStore.entries()) { - totalRequests += total; - agents.add(key.split("::")[0]); - } res.json({ totalServices: servicesStore.size, totalApiKeys: apiKeyStore.size, - totalRequests, - uniqueAgents: agents.size, + totalRequests: getUsageTotalRequests(), + uniqueAgents: usageByAgent.size, paused: pauseState.paused, }); }); From 164dbcb7e2070a7ebb80854bd64cff07f1136506 Mon Sep 17 00:00:00 2001 From: felixvippp-ai Date: Wed, 24 Jun 2026 11:56:33 -0400 Subject: [PATCH 2/5] perf: use service usage indexes --- src/routes/services.ts | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/routes/services.ts b/src/routes/services.ts index d4c3a6e..11a7e05 100644 --- a/src/routes/services.ts +++ b/src/routes/services.ts @@ -4,7 +4,9 @@ import { servicesDisabled, servicesMetadata, servicesStore, + usageByService, usageStore, + usageTotalsByService, } from "../store/state.js"; import { getRequestId } from "../types.js"; @@ -80,15 +82,8 @@ export function createServicesRouter(): Router { router.get("/api/v1/services/:serviceId/usage", (req: Request, res: Response) => { const { serviceId } = req.params; - const suffix = `::${serviceId}`; - let total = 0; - let agents = 0; - for (const [key, value] of usageStore.entries()) { - if (key.endsWith(suffix)) { - total += value; - agents++; - } - } + const total = usageTotalsByService.get(serviceId) ?? 0; + const agents = usageByService.get(serviceId)?.size ?? 0; res.json({ serviceId, total, agents }); }); @@ -102,10 +97,11 @@ export function createServicesRouter(): Router { ); const suffix = `::${serviceId}`; const items: { agent: string; total: number }[] = []; - for (const [key, total] of usageStore.entries()) { - if (key.endsWith(suffix)) { - items.push({ agent: key.slice(0, key.length - suffix.length), total }); - } + for (const key of usageByService.get(serviceId) ?? []) { + items.push({ + agent: key.slice(0, key.length - suffix.length), + total: usageStore.get(key) ?? 0, + }); } items.sort((a, b) => b.total - a.total); res.json({ serviceId, items: items.slice(0, limit) }); @@ -116,10 +112,11 @@ export function createServicesRouter(): Router { const { serviceId } = req.params; const suffix = `::${serviceId}`; const items: { agent: string; total: number }[] = []; - for (const [key, total] of usageStore.entries()) { - if (key.endsWith(suffix)) { - items.push({ agent: key.slice(0, key.length - suffix.length), total }); - } + for (const key of usageByService.get(serviceId) ?? []) { + items.push({ + agent: key.slice(0, key.length - suffix.length), + total: usageStore.get(key) ?? 0, + }); } res.json({ serviceId, items }); }); From 7e40a65ed19a7d39f868c9e6ebe0948eb8766a69 Mon Sep 17 00:00:00 2001 From: felixvippp-ai Date: Wed, 24 Jun 2026 11:56:45 -0400 Subject: [PATCH 3/5] perf: use indexed usage rollups --- src/routes/usage.ts | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/routes/usage.ts b/src/routes/usage.ts index 13b5415..1785261 100644 --- a/src/routes/usage.ts +++ b/src/routes/usage.ts @@ -3,8 +3,11 @@ import { recordEvent } from "../events.js"; import { servicesDisabled, servicesStore, + usageByAgent, usageKey, usageStore, + usageTotalsByAgent, + usageTotalsByService, } from "../store/state.js"; import { getRequestId } from "../types.js"; @@ -144,8 +147,7 @@ export function createUsageRouter(): Router { router.get("/api/v1/billing/total", (_req, res: Response) => { let totalStroops = 0; - for (const [key, requests] of usageStore.entries()) { - const [, serviceId] = key.split("::"); + for (const [serviceId, requests] of usageTotalsByService.entries()) { const price = servicesStore.get(serviceId)?.priceStroops ?? 0; totalStroops += requests * price; } @@ -190,19 +192,13 @@ export function createUsageRouter(): Router { 1000, Math.max(1, Number((req.query.limit as string) ?? 200)) ); - const seen = new Set(); - for (const key of usageStore.keys()) seen.add(key.split("::")[0]); - const agents = Array.from(seen).slice(0, limit); + const agents = Array.from(usageByAgent.keys()).slice(0, limit); res.json({ agents }); }); router.get("/api/v1/agents/:agent/total", (req: Request, res: Response) => { const { agent } = req.params; - const prefix = `${agent}::`; - let total = 0; - for (const [key, n] of usageStore.entries()) { - if (key.startsWith(prefix)) total += n; - } + const total = usageTotalsByAgent.get(agent) ?? 0; res.json({ agent, total }); }); @@ -210,10 +206,11 @@ export function createUsageRouter(): Router { const { agent } = req.params; const prefix = `${agent}::`; const items: { serviceId: string; total: number }[] = []; - for (const [key, total] of usageStore.entries()) { - if (key.startsWith(prefix)) { - items.push({ serviceId: key.slice(prefix.length), total }); - } + for (const key of usageByAgent.get(agent) ?? []) { + items.push({ + serviceId: key.slice(prefix.length), + total: usageStore.get(key) ?? 0, + }); } res.json({ agent, items }); }); From d6bc502c53d10bc68113203a5adc540557bc0220 Mon Sep 17 00:00:00 2001 From: felixvippp-ai Date: Wed, 24 Jun 2026 11:57:02 -0400 Subject: [PATCH 4/5] perf: maintain usage rollup indexes --- src/store/state.ts | 150 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 3 deletions(-) diff --git a/src/store/state.ts b/src/store/state.ts index 6c573f4..f1b2ab5 100644 --- a/src/store/state.ts +++ b/src/store/state.ts @@ -23,12 +23,156 @@ export const config: Record = { /** Opaque API keys keyed by full secret token. */ export const apiKeyStore = new Map(); -/** Outstanding usage counters keyed by `${agent}::${serviceId}`. */ -export const usageStore = new Map(); - /** Builds the shared in-memory usage key for an agent/service pair. */ export const usageKey = (agent: string, serviceId: string) => `${agent}::${serviceId}`; +type UsageKeyParts = { agent: string; serviceId: string }; + +const parseUsageKey = (key: string): UsageKeyParts | undefined => { + const [agent, serviceId] = key.split("::"); + if (!agent || serviceId === undefined) return undefined; + return { agent, serviceId }; +}; + +const addKey = (index: Map>, bucket: string, key: string) => { + const keys = index.get(bucket); + if (keys) { + keys.add(key); + return; + } + index.set(bucket, new Set([key])); +}; + +const removeKey = (index: Map>, bucket: string, key: string) => { + const keys = index.get(bucket); + if (!keys) return; + keys.delete(key); + if (keys.size === 0) index.delete(bucket); +}; + +const addTotal = (totals: Map, bucket: string, delta: number) => { + totals.set(bucket, (totals.get(bucket) ?? 0) + delta); +}; + +/** + * Usage keys grouped by agent so agent rollups avoid scanning the full store. + */ +export const usageByAgent = new Map>(); + +/** + * Usage keys grouped by service so service rollups avoid scanning the full store. + */ +export const usageByService = new Map>(); + +/** Outstanding request totals grouped by agent. */ +export const usageTotalsByAgent = new Map(); + +/** Outstanding request totals grouped by service. */ +export const usageTotalsByService = new Map(); + +let usageTotalRequests = 0; + +/** + * Returns the protocol-wide outstanding request total maintained on writes. + */ +export const getUsageTotalRequests = () => usageTotalRequests; + +/** + * Verifies the maintained usage indexes against a brute-force store scan. + * This is intended for regression tests and health checks, not request paths. + */ +export const assertUsageIndexesConsistent = () => { + const expectedByAgent = new Map>(); + const expectedByService = new Map>(); + const expectedAgentTotals = new Map(); + const expectedServiceTotals = new Map(); + let expectedTotal = 0; + + for (const [key, total] of usageStore.entries()) { + expectedTotal += total; + const parts = parseUsageKey(key); + if (!parts) continue; + addKey(expectedByAgent, parts.agent, key); + addKey(expectedByService, parts.serviceId, key); + addTotal(expectedAgentTotals, parts.agent, total); + addTotal(expectedServiceTotals, parts.serviceId, total); + } + + const serializeSets = (index: Map>) => + JSON.stringify( + Array.from(index.entries()).map(([bucket, keys]) => [ + bucket, + Array.from(keys).sort(), + ]) + ); + const serializeTotals = (totals: Map) => + JSON.stringify(Array.from(totals.entries())); + + if ( + usageTotalRequests !== expectedTotal || + serializeSets(usageByAgent) !== serializeSets(expectedByAgent) || + serializeSets(usageByService) !== serializeSets(expectedByService) || + serializeTotals(usageTotalsByAgent) !== serializeTotals(expectedAgentTotals) || + serializeTotals(usageTotalsByService) !== serializeTotals(expectedServiceTotals) + ) { + throw new Error("usage indexes are inconsistent with usageStore"); + } +}; + +class IndexedUsageStore extends Map { + set(key: string, value: number): this { + const previous = super.get(key) ?? 0; + const existed = super.has(key); + super.set(key, value); + + const delta = value - previous; + usageTotalRequests += delta; + const parts = parseUsageKey(key); + if (!parts) return this; + + if (!existed) { + addKey(usageByAgent, parts.agent, key); + addKey(usageByService, parts.serviceId, key); + } + addTotal(usageTotalsByAgent, parts.agent, delta); + addTotal(usageTotalsByService, parts.serviceId, delta); + + return this; + } + + delete(key: string): boolean { + const previous = super.get(key); + const deleted = super.delete(key); + if (!deleted) return false; + + usageTotalRequests -= previous ?? 0; + const parts = parseUsageKey(key); + if (!parts) return true; + + removeKey(usageByAgent, parts.agent, key); + removeKey(usageByService, parts.serviceId, key); + addTotal(usageTotalsByAgent, parts.agent, -(previous ?? 0)); + addTotal(usageTotalsByService, parts.serviceId, -(previous ?? 0)); + if (!usageByAgent.has(parts.agent)) usageTotalsByAgent.delete(parts.agent); + if (!usageByService.has(parts.serviceId)) + usageTotalsByService.delete(parts.serviceId); + + return true; + } + + clear(): void { + super.clear(); + usageByAgent.clear(); + usageByService.clear(); + usageTotalsByAgent.clear(); + usageTotalsByService.clear(); + usageTotalRequests = 0; + } +} + +/** Outstanding usage counters keyed by `${agent}::${serviceId}`. */ +export const usageStore = new IndexedUsageStore(); + /** Registered services and their per-request prices. */ export const servicesStore = new Map(); From e9e3a76efb483567aa3ede3759250a22ca816f3a Mon Sep 17 00:00:00 2001 From: felixvippp-ai Date: Wed, 24 Jun 2026 11:57:30 -0400 Subject: [PATCH 5/5] test: cover usage rollup indexes --- src/usage-index.test.ts | 130 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 src/usage-index.test.ts diff --git a/src/usage-index.test.ts b/src/usage-index.test.ts new file mode 100644 index 0000000..713ed53 --- /dev/null +++ b/src/usage-index.test.ts @@ -0,0 +1,130 @@ +import { describe, it, beforeEach } from "node:test"; +import assert from "node:assert"; +import request from "supertest"; +import { createApp } from "./index.js"; +import { + assertUsageIndexesConsistent, + getUsageTotalRequests, + servicesDisabled, + servicesMetadata, + servicesStore, + usageByAgent, + usageByService, + usageStore, + usageTotalsByAgent, + usageTotalsByService, +} from "./store/state.js"; + +beforeEach(() => { + servicesDisabled.clear(); + servicesMetadata.clear(); + servicesStore.clear(); + usageStore.clear(); +}); + +void describe("usage rollup indexes", () => { + void it("keeps indexes in sync after add, bulk, settle, overwrite, and clear", async () => { + const app = createApp(); + servicesStore.set("svc-alpha", { priceStroops: 1 }); + servicesStore.set("svc-beta", { priceStroops: 2 }); + + await request(app) + .post("/api/v1/usage") + .send({ agent: "agent-a", serviceId: "svc-alpha", requests: 3 }) + .expect(201); + await request(app) + .post("/api/v1/usage") + .send({ agent: "agent-a", serviceId: "svc-beta", requests: 4 }) + .expect(201); + await request(app) + .post("/api/v1/usage/bulk") + .send({ + items: [ + { agent: "agent-b", serviceId: "svc-alpha", requests: 5 }, + { agent: "agent-b", serviceId: "svc-beta", requests: 0 }, + ], + }) + .expect(201); + + assertUsageIndexesConsistent(); + assert.strictEqual(getUsageTotalRequests(), 12); + assert.strictEqual(usageTotalsByAgent.get("agent-a"), 7); + assert.strictEqual(usageTotalsByService.get("svc-alpha"), 8); + assert.deepStrictEqual(Array.from(usageByAgent.keys()), ["agent-a", "agent-b"]); + assert.deepStrictEqual(Array.from(usageByService.keys()), [ + "svc-alpha", + "svc-beta", + ]); + + const stats = await request(app).get("/api/v1/stats").expect(200); + assert.strictEqual(stats.body.totalRequests, 12); + assert.strictEqual(stats.body.uniqueAgents, 2); + + const billingTotal = await request(app).get("/api/v1/billing/total").expect(200); + assert.strictEqual(billingTotal.body.totalStroops, 16); + + const settled = await request(app) + .post("/api/v1/settle") + .send({ agent: "agent-a", serviceId: "svc-alpha" }) + .expect(200); + assert.strictEqual(settled.body.requests, 3); + + assertUsageIndexesConsistent(); + assert.strictEqual(getUsageTotalRequests(), 9); + assert.strictEqual(usageTotalsByAgent.get("agent-a"), 4); + assert.strictEqual(usageTotalsByService.get("svc-alpha"), 5); + + usageStore.set("agent-b::svc-alpha", 8); + assertUsageIndexesConsistent(); + assert.strictEqual(getUsageTotalRequests(), 12); + assert.strictEqual(usageTotalsByAgent.get("agent-b"), 8); + assert.strictEqual(usageTotalsByService.get("svc-alpha"), 8); + + usageStore.clear(); + assertUsageIndexesConsistent(); + assert.strictEqual(getUsageTotalRequests(), 0); + assert.strictEqual(usageByAgent.size, 0); + assert.strictEqual(usageByService.size, 0); + }); + + void it("serves agent and service rollups from maintained indexes", async () => { + const app = createApp(); + + await request(app) + .post("/api/v1/usage/bulk") + .send({ + items: [ + { agent: "agent-a", serviceId: "svc-shared", requests: 2 }, + { agent: "agent-b", serviceId: "svc-shared", requests: 7 }, + { agent: "agent-a", serviceId: "svc-other", requests: 4 }, + ], + }) + .expect(201); + + const agentTotal = await request(app) + .get("/api/v1/agents/agent-a/total") + .expect(200); + assert.strictEqual(agentTotal.body.total, 6); + + const agentUsage = await request(app) + .get("/api/v1/agents/agent-a/usage") + .expect(200); + assert.deepStrictEqual(agentUsage.body.items, [ + { serviceId: "svc-shared", total: 2 }, + { serviceId: "svc-other", total: 4 }, + ]); + + const serviceUsage = await request(app) + .get("/api/v1/services/svc-shared/usage") + .expect(200); + assert.strictEqual(serviceUsage.body.total, 9); + assert.strictEqual(serviceUsage.body.agents, 2); + + const topAgents = await request(app) + .get("/api/v1/services/svc-shared/agents/top?limit=1") + .expect(200); + assert.deepStrictEqual(topAgents.body.items, [{ agent: "agent-b", total: 7 }]); + + assertUsageIndexesConsistent(); + }); +});