perf: batch-load app history to eliminate N+1 fetches on board load

Previously each AppWidget fetched /api/apps/{id}/history individually
on mount, causing N sequential HTTP requests. Now the board page
server load fetches all app histories in a single Prisma query via
getBatchStatusHistory() and passes them to AppWidget via Svelte
context. AppWidget uses the pre-loaded data immediately with a
fallback fetch for non-board contexts.
This commit is contained in:
2026-03-25 15:36:06 +03:00
parent 6eb6bba289
commit 92eeeadec0
4 changed files with 66 additions and 5 deletions
+10 -4
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte';
import { onMount, getContext } from 'svelte';
import AppHealthBadge from '$lib/components/app/AppHealthBadge.svelte';
import AnimatedStatusRing from '$lib/components/app/AnimatedStatusRing.svelte';
import SparklineChart from '$lib/components/app/SparklineChart.svelte';
@@ -48,9 +48,13 @@
const cardStyleClass = $derived(`card-${theme.cardStyle}`);
let historyData: StatusPoint[] = $state([]);
let uptimePercent: number | null = $state(null);
let historyLoading = $state(true);
// Use pre-loaded history from context (set by board page) to avoid N+1 fetches
const appHistories = getContext<Record<string, { history: StatusPoint[]; uptimePercent: number }> | undefined>('appHistories');
const preloaded = appHistories?.[app.id];
let historyData: StatusPoint[] = $state(preloaded?.history ?? []);
let uptimePercent: number | null = $state(preloaded?.uptimePercent ?? null);
let historyLoading = $state(!preloaded);
let linksExpanded = $state(false);
let showContextMenu = $state(false);
let contextMenuPos = $state({ x: 0, y: 0 });
@@ -74,7 +78,9 @@
}
});
// Fallback: fetch history only if not pre-loaded via context
onMount(async () => {
if (preloaded) return;
try {
const res = await fetch(`/api/apps/${app.id}/history`);
if (res.ok) {