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:
@@ -127,6 +127,38 @@ export async function getStatusHistory(appId: string, limit: number = 50) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch-fetch status history for multiple apps in a single query.
|
||||
* Returns a map of appId -> { history, uptimePercent }.
|
||||
*/
|
||||
export async function getBatchStatusHistory(
|
||||
appIds: readonly string[],
|
||||
limitPerApp: number = 288
|
||||
): Promise<ReadonlyMap<string, { readonly history: readonly { status: string; responseTime: number | null; checkedAt: Date }[]; readonly uptimePercent: number }>> {
|
||||
if (appIds.length === 0) return new Map();
|
||||
|
||||
const allStatuses = await prisma.appStatus.findMany({
|
||||
where: { appId: { in: [...appIds] } },
|
||||
orderBy: { checkedAt: 'desc' },
|
||||
select: { appId: true, status: true, responseTime: true, checkedAt: true }
|
||||
});
|
||||
|
||||
const result = new Map<string, { history: { status: string; responseTime: number | null; checkedAt: Date }[]; uptimePercent: number }>();
|
||||
|
||||
for (const appId of appIds) {
|
||||
const statuses = allStatuses.filter((s) => s.appId === appId).slice(0, limitPerApp).reverse();
|
||||
const totalChecks = statuses.length;
|
||||
const onlineChecks = statuses.filter((s) => s.status === 'online').length;
|
||||
const uptimePercent = totalChecks > 0 ? Math.round((onlineChecks / totalChecks) * 1000) / 10 : 0;
|
||||
result.set(appId, {
|
||||
history: statuses.map((s) => ({ status: s.status, responseTime: s.responseTime, checkedAt: s.checkedAt })),
|
||||
uptimePercent
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getHealthcheckTargets() {
|
||||
return prisma.app.findMany({
|
||||
where: { healthcheckEnabled: true },
|
||||
|
||||
Reference in New Issue
Block a user