From 129de13f469c398e67b53f6c7552dab9a7525085 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2026 17:39:29 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[performance=20improvement]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 💡 What: Replace JS in-memory counting with database aggregation (COUNT, GTE, INARRAY) for Dashboard KPIs. 🎯 Why: The previous logic fetched every task and issue from the database into memory, causing an N+1 scaling bottleneck as projects grew. 📊 Impact: Eliminates large full-table data transfers and JS filtering overloads, offloading work to the database and improving endpoint speed linearly. 🔬 Measurement: Monitor KPI dashboard loading time and observe reduced CPU / memory usage on the Node server. Co-authored-by: bobdivx <6737167+bobdivx@users.noreply.github.com> --- .jules/bolt.md | 3 +++ src/pages/api/dashboard-kpis.ts | 37 +++++++++++++-------------------- 2 files changed, 18 insertions(+), 22 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..1bde883b --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-08-24 - Database Aggregation +**Learning:** Fetching full tables into JS memory and using `.filter().length` causes OOM exceptions and severe CPU bottlenecks at scale. +**Action:** Use Astro DB (Drizzle) aggregations like `count()`, `inArray()`, and `gte()` directly in the database queries instead. diff --git a/src/pages/api/dashboard-kpis.ts b/src/pages/api/dashboard-kpis.ts index 5cf2dd56..4ea91eda 100644 --- a/src/pages/api/dashboard-kpis.ts +++ b/src/pages/api/dashboard-kpis.ts @@ -19,10 +19,6 @@ function countRunningSessions(sessions: unknown[]): number { }).length; } -function isOpenQueueStatus(st: string): boolean { - const s = String(st).toLowerCase(); - return s === 'open' || s === 'in_progress'; -} export const GET: APIRoute = async () => { const base = { @@ -42,30 +38,27 @@ export const GET: APIRoute = async () => { }; try { - const { db, Project, AgentTask, Request, AgentAppIssue, AgentDependencyRequest, eq } = + const { db, Project, AgentTask, Request, AgentAppIssue, AgentDependencyRequest, eq, count, inArray, gte } = await loadAstroDb(); const today = new Date(); today.setHours(0, 0, 0, 0); - const [projects, tasksAll, openRequests, issuesAll, depsAll] = await Promise.all([ - db.select().from(Project), - db.select().from(AgentTask), - db.select().from(Request).where(eq(Request.status, 'pending')), - db.select().from(AgentAppIssue), - db.select().from(AgentDependencyRequest), + // ⚡ Bolt Optimization: Use DB aggregation (count, gte, inArray) instead of fetching full tables into memory + const [projectsRes, tasksAllRes, tasksTodayRes, openReqsRes, issuesRes, depsRes] = await Promise.all([ + db.select({ value: count() }).from(Project), + db.select({ value: count() }).from(AgentTask), + db.select({ value: count() }).from(AgentTask).where(gte(AgentTask.createdAt, today)), + db.select({ value: count() }).from(Request).where(eq(Request.status, 'pending')), + db.select({ value: count() }).from(AgentAppIssue).where(inArray(AgentAppIssue.status, ['open', 'in_progress'])), + db.select({ value: count() }).from(AgentDependencyRequest).where(inArray(AgentDependencyRequest.status, ['open', 'in_progress'])), ]); - const tasksTodayCount = tasksAll.filter((t) => { - const d = t.createdAt instanceof Date ? t.createdAt : new Date(t.createdAt as Date); - return d >= today; - }).length; - - base.projectCount = projects.length; - base.tasksTotal = tasksAll.length; - base.tasksToday = tasksTodayCount; - base.openRequests = openRequests.length; - base.openAppIssues = issuesAll.filter((r) => isOpenQueueStatus(String(r.status))).length; - base.openDependencyRequests = depsAll.filter((r) => isOpenQueueStatus(String(r.status))).length; + base.projectCount = projectsRes[0]?.value || 0; + base.tasksTotal = tasksAllRes[0]?.value || 0; + base.tasksToday = tasksTodayRes[0]?.value || 0; + base.openRequests = openReqsRes[0]?.value || 0; + base.openAppIssues = issuesRes[0]?.value || 0; + base.openDependencyRequests = depsRes[0]?.value || 0; } catch (e) { const msg = e instanceof Error ? e.message : String(e); base.dbError = msg;