diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..1eecbeb4 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2025-05-28 - Parallelizing Gateway Calls and DB Queries +**Learning:** In Astro API routes serving frontend dashboards, sequentially executing slow external checks (like `fetchZimaOSSessionsPayload` and `getPrimaryDevServerStatus`) after database operations acts as a severe performance bottleneck because the network requests wait idly during DB I/O. +**Action:** Always initiate slow external network or gateway calls at the very beginning of the request handler (`const fetchPromise = fetchSomething().catch(e => e)`) so they execute concurrently with local Drizzle ORM queries (`Promise.all([db.select()...])`), and `await` the external response only at the end before returning the payload. Additionally, when processing independent I/O checks across a list of database results (like scanning projects), map the logic into an array of Promises and sequentially `await` them inside a `for` loop to preserve order while maximizing concurrency. diff --git a/src/pages/api/dashboard-kpis.ts b/src/pages/api/dashboard-kpis.ts index 5cf2dd56..fc00550b 100644 --- a/src/pages/api/dashboard-kpis.ts +++ b/src/pages/api/dashboard-kpis.ts @@ -25,6 +25,9 @@ function isOpenQueueStatus(st: string): boolean { } export const GET: APIRoute = async () => { + // Démarre la requête gateway ZimaOS le plus tôt possible pour qu'elle s'exécute en parallèle avec la DB + const zimaosFetchPromise = fetchZimaOSSessionsPayload(undefined).catch((e) => e); + const base = { projectCount: 0, tasksTotal: 0, @@ -76,7 +79,10 @@ export const GET: APIRoute = async () => { } try { - const zimaosResult = await fetchZimaOSSessionsPayload(undefined); + const zimaosResult = await zimaosFetchPromise; + if (zimaosResult instanceof Error) { + throw zimaosResult; + } const ocSessions = zimaosResult.ok ? (normalizeZimaOSSessions(zimaosResult.data) as Record[]) : []; diff --git a/src/pages/api/dashboard-projects-health.ts b/src/pages/api/dashboard-projects-health.ts index c9947031..e7d0fc7e 100644 --- a/src/pages/api/dashboard-projects-health.ts +++ b/src/pages/api/dashboard-projects-health.ts @@ -31,6 +31,9 @@ function mapSessionToRunState(raw: Record): { running: boolean export const GET: APIRoute = async ({ locals }) => { const email = locals.user?.email as string | undefined; + // Démarre la requête gateway ZimaOS le plus tôt possible pour qu'elle s'exécute en parallèle + const zimaosFetchPromise = fetchZimaOSSessionsPayload(email).catch((e) => e); + const payload: { projects: Array<{ id: number; @@ -69,9 +72,16 @@ export const GET: APIRoute = async ({ locals }) => { try { const { db, Project, AgentTask } = await loadAstroDb(); - const projects = await db.select().from(Project).orderBy(desc(Project.updatedAt)).limit(12); - const tasksAll = await db.select().from(AgentTask).limit(500); + // Parallélise les requêtes DB (Project et AgentTask) et getWorkSystemStatus + const [projectsRes, tasksAll, workScheduler] = await Promise.all([ + db.select().from(Project).orderBy(desc(Project.updatedAt)).limit(12), + db.select().from(AgentTask).limit(500), + getWorkSystemStatus() + ]); + + const projects = projectsRes as ProjectRow[]; + payload.swarm.workScheduler = workScheduler; const countForProject = (pid: number | null | undefined) => { const pend = tasksAll.filter( @@ -83,7 +93,8 @@ export const GET: APIRoute = async ({ locals }) => { return { pendingOrRunning: pend.length, running }; }; - for (const p of projects as ProjectRow[]) { + // Prépare les promesses pour la résolution I/O des projets en parallèle + const projectPromises = projects.map(async (p) => { let dev: { ok: boolean; running?: boolean; @@ -131,16 +142,20 @@ export const GET: APIRoute = async ({ locals }) => { dev.hint = 'Erreur lecture disque'; } - payload.projects.push({ + return { id: p.id, name: p.name, swarmEnabled: Number(p.swarmEnabled) === 1, devServer: dev, tasks: countForProject(p.id), - }); + }; + }); + + // Attendre les résultats dans le même ordre pour préserver l'ordre du tri DB (desc(Project.updatedAt)) + for (const promise of projectPromises) { + payload.projects.push(await promise); } - payload.swarm.workScheduler = await getWorkSystemStatus(); } catch (e) { payload.dbError = e instanceof Error ? e.message : String(e); return new Response(JSON.stringify(payload), { @@ -150,7 +165,10 @@ export const GET: APIRoute = async ({ locals }) => { } try { - const oc = await fetchZimaOSSessionsPayload(email); + const oc = await zimaosFetchPromise; + if (oc instanceof Error) { + throw oc; + } payload.swarm.zimaosOk = oc.ok; const sessions = oc.ok ? (normalizeZimaOSSessions(oc.data) as Record[])