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:
@@ -75,7 +75,26 @@ export const load: PageServerLoad = async ({ params, locals }) => {
|
||||
}));
|
||||
}
|
||||
|
||||
return { board, canEdit, allApps, users, groups };
|
||||
// Batch-load sparkline history for all apps on this board (single query)
|
||||
const appIdsOnBoard = board.sections
|
||||
.flatMap((s: { widgets: { appId: string | null }[] }) => s.widgets)
|
||||
.map((w: { appId: string | null }) => w.appId)
|
||||
.filter((id: string | null): id is string => id !== null);
|
||||
|
||||
const historyMap = appIdsOnBoard.length > 0
|
||||
? await appService.getBatchStatusHistory(appIdsOnBoard)
|
||||
: new Map();
|
||||
|
||||
// Serialize the Map to a plain object for the client
|
||||
const appHistories: Record<string, { history: { status: string; responseTime: number | null; checkedAt: string }[]; uptimePercent: number }> = {};
|
||||
for (const [appId, data] of historyMap) {
|
||||
appHistories[appId] = {
|
||||
history: data.history.map((h) => ({ ...h, checkedAt: h.checkedAt.toISOString() })),
|
||||
uptimePercent: data.uptimePercent
|
||||
};
|
||||
}
|
||||
|
||||
return { board, canEdit, allApps, users, groups, appHistories };
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Board not found';
|
||||
if (message.includes('not found')) {
|
||||
|
||||
@@ -9,11 +9,15 @@
|
||||
import CustomCssInjector from '$lib/components/layout/CustomCssInjector.svelte';
|
||||
import WallpaperBackground from '$lib/components/background/WallpaperBackground.svelte';
|
||||
import { broadcastDataChange } from '$lib/utils/broadcastSync.js';
|
||||
import { setContext } from 'svelte';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
const boardCardSize = $derived((data.board.cardSize as 'compact' | 'medium' | 'large') ?? 'medium');
|
||||
|
||||
// Provide pre-loaded app histories via context to avoid N+1 fetches in AppWidget
|
||||
setContext('appHistories', data.appHistories ?? {});
|
||||
|
||||
let showShareDialog = $state(false);
|
||||
let guestToggleError = $state('');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user