From e930bc0620c95936b1dc5f7e5aee7c2330a40e7a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 17:51:05 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Batch=20database=20inserts?= =?UTF-8?q?=20to=20resolve=20N+1=20bottlenecks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored multiple files where database inserts were happening inside a loop, replacing them with mapping arrays and batch inserts to improve database efficiency. Co-authored-by: bobdivx <6737167+bobdivx@users.noreply.github.com> --- .jules/bolt.md | 3 +++ src/pages/api/forge-chat-stream.ts | 27 +++++++++++++-------------- src/pages/api/forge-chat.ts | 27 +++++++++++++-------------- src/pages/api/models.ts | 12 +++++++----- 4 files changed, 36 insertions(+), 33 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..1b5b32ab --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2025-05-15 - Batch Database Inserts to Avoid N+1 Bottlenecks +**Learning:** Found multiple instances where the application iterates over an array (e.g., missing seed records, generated request plans) and executes a separate `await db.insert(...).values(...)` for each item. This N+1 query pattern creates an unnecessary performance bottleneck, especially for bulk operations or initialization tasks. +**Action:** Replaced iterative single-record `insert` statements with `map` operations to construct an array of objects, then passed the entire array into a single `await db.insert(...).values([...])` call. This leverages Drizzle ORM's native batch insert capabilities and significantly reduces database round-trips. diff --git a/src/pages/api/forge-chat-stream.ts b/src/pages/api/forge-chat-stream.ts index afc0b5b1..14016142 100644 --- a/src/pages/api/forge-chat-stream.ts +++ b/src/pages/api/forge-chat-stream.ts @@ -114,20 +114,19 @@ export const POST: APIRoute = async ({ request, locals }) => { if (orchestrated.plan && orchestrated.plan.length > 0 && projectId) { const { Request } = await loadAstroDb(); - for (const item of orchestrated.plan) { - await db.insert(Request).values({ - projectId, - title: item.title, - content: item.content || `Tâche issue du plan de ${agentId}`, - status: 'pending', - priority: 'medium', - author: agentId, - requestType: 'Correction', - assigneeAgentId: item.assignee || undefined, - createdAt: new Date(), - updatedAt: new Date(), - }); - } + const planRequests = orchestrated.plan.map((item) => ({ + projectId, + title: item.title, + content: item.content || `Tâche issue du plan de ${agentId}`, + status: 'pending', + priority: 'medium', + author: agentId, + requestType: 'Correction', + assigneeAgentId: item.assignee || undefined, + createdAt: new Date(), + updatedAt: new Date(), + })); + await db.insert(Request).values(planRequests); } send('done', { diff --git a/src/pages/api/forge-chat.ts b/src/pages/api/forge-chat.ts index f8f7a804..df68f0f7 100644 --- a/src/pages/api/forge-chat.ts +++ b/src/pages/api/forge-chat.ts @@ -80,20 +80,19 @@ export const POST: APIRoute = async ({ request, locals }) => { if (orchestrated.plan && orchestrated.plan.length > 0 && projectId) { const { Request } = await loadAstroDb(); - for (const item of orchestrated.plan) { - await db.insert(Request).values({ - projectId, - title: item.title, - content: item.content || `Tâche issue du plan de ${agentId}`, - status: 'pending', - priority: 'medium', - author: agentId, - requestType: 'Correction', - assigneeAgentId: item.assignee || undefined, - createdAt: new Date(), - updatedAt: new Date(), - }); - } + const planRequests = orchestrated.plan.map((item) => ({ + projectId, + title: item.title, + content: item.content || `Tâche issue du plan de ${agentId}`, + status: 'pending', + priority: 'medium', + author: agentId, + requestType: 'Correction', + assigneeAgentId: item.assignee || undefined, + createdAt: new Date(), + updatedAt: new Date(), + })); + await db.insert(Request).values(planRequests); } return new Response( diff --git a/src/pages/api/models.ts b/src/pages/api/models.ts index 12a66889..a3b6200c 100644 --- a/src/pages/api/models.ts +++ b/src/pages/api/models.ts @@ -44,17 +44,19 @@ export const GET: APIRoute = async ({ request }) => { dbCatalog = await db.select().from(AgentModel); } else { const existing = new Set(dbCatalog.map((m) => String(m.id).trim())); - for (const m of FORGE_DEFAULT_AGENT_MODELS) { - if (existing.has(m.id)) continue; - await db.insert(AgentModel).values({ + const toInsert = FORGE_DEFAULT_AGENT_MODELS + .filter((m) => !existing.has(m.id)) + .map((m) => ({ id: m.id, label: m.label, source: 'seed', enabled: 1, updatedAt: now, - }); + })); + if (toInsert.length > 0) { + await db.insert(AgentModel).values(toInsert); + dbCatalog = await db.select().from(AgentModel); } - dbCatalog = await db.select().from(AgentModel); } dbEnabled = dbCatalog .filter((m) => Number(m.enabled) === 1)