From bb2df61223b2d65f0e255619292e1f96ae6d6d77 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 5 Jun 2026 21:36:19 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20usage=20records?= =?UTF-8?q?=20aggregation=20loops?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored `getSessionTotals` and related route logic to process Prisma relation arrays (`session.usageRecords`) using a single `for...of` loop instead of chained `.reduce()` passes. This eliminates multiple iteration cycles and reduces memory overhead. --- .jules/bolt.md | 3 +++ .../dashboard/sessions/[sessionId]/route.ts | 12 ++++++++--- .../[orgSlug]/dashboard/sessions/route.ts | 20 +++++++++++++------ 3 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..99657df --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2026-06-05 - Avoid multiple iteration passes on Prisma include arrays +**Learning:** When aggregating metrics from large Prisma relational arrays (`include`), chaining multiple `.reduce()` calls or performing multiple passes causes high iteration overhead and potential memory bottlenecks. +**Action:** Always prefer a single `for...of` loop or a single accumulator pass when calculating multiple summary metrics (like token counts and costs) over the same relation dataset. diff --git a/packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/[sessionId]/route.ts b/packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/[sessionId]/route.ts index 35486b8..3d7061e 100644 --- a/packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/[sessionId]/route.ts +++ b/packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/[sessionId]/route.ts @@ -43,9 +43,15 @@ export async function GET( return forbiddenByRole(access.role, '본인 세션만 열람 가능') } - const totalInput = session.usageRecords.reduce((sum, r) => sum + r.inputTokens, 0) - const totalOutput = session.usageRecords.reduce((sum, r) => sum + r.outputTokens, 0) - const totalCost = session.usageRecords.reduce((sum, r) => sum + (r.estimatedCostUsd ?? 0), 0) + // ⚡ Bolt: single loop aggregation over relation array to prevent multi-pass overhead + let totalInput = 0 + let totalOutput = 0 + let totalCost = 0 + for (const r of session.usageRecords) { + totalInput += r.inputTokens + totalOutput += r.outputTokens + totalCost += r.estimatedCostUsd ?? 0 + } const usageTimeline: SessionTimelineUsage[] = session.usageRecords.map((r) => ({ timestamp: r.timestamp.toISOString(), diff --git a/packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts b/packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts index 25fbe70..bc52a07 100644 --- a/packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts +++ b/packages/web/src/app/api/orgs/[orgSlug]/dashboard/sessions/route.ts @@ -32,14 +32,22 @@ const sessionInclude = { type SessionWithInclude = Prisma.ClaudeSessionGetPayload<{ include: typeof sessionInclude }> +// ⚡ Bolt: single loop aggregation over relation array to prevent multi-pass overhead function getSessionTotals(session: SessionWithInclude) { + let inputTokens = 0 + let outputTokens = 0 + let estimatedCostUsd = 0 + + for (const r of session.usageRecords) { + inputTokens += r.inputTokens + outputTokens += r.outputTokens + estimatedCostUsd += r.estimatedCostUsd ?? 0 + } + return { - inputTokens: session.usageRecords.reduce((sum, r) => sum + r.inputTokens, 0), - outputTokens: session.usageRecords.reduce((sum, r) => sum + r.outputTokens, 0), - estimatedCostUsd: session.usageRecords.reduce( - (sum, r) => sum + (r.estimatedCostUsd ?? 0), - 0, - ), + inputTokens, + outputTokens, + estimatedCostUsd, } }