perf(web): batch dashboard tile hydration

This commit is contained in:
vince 2026-06-20 15:12:18 +02:00
parent 63b008095f
commit de87464ef1
7 changed files with 518 additions and 40 deletions

View file

@ -52,6 +52,11 @@ type DashboardTileResponse =
message: string;
};
type DashboardTilesBatchResponse = {
state: "ready";
tiles: DashboardTileResponse[];
};
type DashboardTileHydrationResult = "aborted" | "failed" | "ready";
const dashboardTileHydrationConcurrency = 6;
@ -292,6 +297,7 @@ export default function App() {
const modelIds = tiles.map(dashboardTileModelId);
void runViewportAwareDashboardHydrationQueue({
batchSize: dashboardTileHydrationConcurrency,
collectVisibleModelIds: async () => {
await waitForDashboardRenderFrame(signal);
return collectVisibleDashboardModelIds({
@ -321,6 +327,23 @@ export default function App() {
tileBackoff.recordFailure(key);
}
},
hydrateBatch: async (batch) => {
const results = await hydrateDashboardTileBatch(
batch,
run,
signal,
snapshotContext,
);
for (const { result, tile } of results) {
const key = dashboardTileKey(tile);
if (result === "ready") {
tileBackoff.recordSuccess(key);
} else if (result === "failed") {
tileBackoff.recordFailure(key);
}
}
},
items: tiles,
onAllItemsSettled: () => performanceMarks.markAllTilesSettled(),
onVisibleItemsSettled: () => performanceMarks.markVisibleTilesReady(),
@ -335,6 +358,69 @@ export default function App() {
});
}
async function hydrateDashboardTileBatch(
tiles: DashboardTileReference[],
run: number,
signal: AbortSignal,
snapshotContext: DashboardTileSnapshotStoreContext,
): Promise<Array<{ result: DashboardTileHydrationResult; tile: DashboardTileReference }>> {
try {
const response = await fetch("/api/dashboard/tiles", {
body: JSON.stringify({ tiles }),
headers: { "Content-Type": "application/json" },
method: "POST",
signal,
});
const batchResponse = (await response.json()) as DashboardTilesBatchResponse;
if (
cancelled ||
signal.aborted ||
run !== hydrationRun ||
!response.ok ||
batchResponse.state !== "ready"
) {
throw new Error("Dashboard tile batch hydration failed");
}
const responsesByKey = new Map(
batchResponse.tiles.map((tileResponse) => [
dashboardTileKey(tileResponse.tile),
tileResponse,
]),
);
return tiles.map((tile) => {
const key = dashboardTileKey(tile);
try {
return {
tile,
result: applyDashboardTileHydrationResponse(
tile,
responsesByKey.get(key),
run,
signal,
snapshotContext,
),
};
} finally {
finishDashboardTileHydration(key, run, signal);
}
});
} catch (error) {
if (signal.aborted || cancelled || run !== hydrationRun) {
return tiles.map((tile) => ({ result: "aborted", tile }));
}
return Promise.all(
tiles.map(async (tile) => ({
tile,
result: await hydrateDashboardTile(tile, run, signal, snapshotContext),
})),
);
}
}
async function hydrateDashboardTile(
tile: DashboardTileReference,
run: number,
@ -347,32 +433,23 @@ export default function App() {
const response = await fetch(dashboardTileUrl(tile), { signal });
const tileResponse = (await response.json()) as DashboardTileResponse;
if (
cancelled ||
signal.aborted ||
run !== hydrationRun ||
!response.ok ||
tileResponse.state !== "ready"
) {
if (!response.ok) {
return "failed";
}
const result = applyDashboardTileHydrationResponse(
tile,
tileResponse,
run,
signal,
snapshotContext,
);
if (result !== "ready") {
return signal.aborted || cancelled || run !== hydrationRun
? "aborted"
: "failed";
}
const readyTileResponse = tileResponse;
setDashboard((current) =>
current?.state === "ready"
? {
...current,
document: applyDashboardTile(current.document, readyTileResponse),
}
: current,
);
tileSnapshotStore.saveReadyTile({
...snapshotContext,
response: readyTileResponse,
});
performanceMarks.markFirstTileReady();
return "ready";
} catch (error) {
if (!isAbortError(error) && !cancelled) {
@ -381,13 +458,58 @@ export default function App() {
}
return "aborted";
} finally {
if (!cancelled && !signal.aborted && run === hydrationRun) {
setHydratingItemIds((current) => {
const next = new Set(current);
next.delete(key);
return next;
});
}
finishDashboardTileHydration(key, run, signal);
}
}
function applyDashboardTileHydrationResponse(
tile: DashboardTileReference,
tileResponse: DashboardTileResponse | undefined,
run: number,
signal: AbortSignal,
snapshotContext: DashboardTileSnapshotStoreContext,
): DashboardTileHydrationResult {
if (
cancelled ||
signal.aborted ||
run !== hydrationRun ||
!tileResponse ||
dashboardTileKey(tileResponse.tile) !== dashboardTileKey(tile) ||
tileResponse.state !== "ready"
) {
return signal.aborted || cancelled || run !== hydrationRun
? "aborted"
: "failed";
}
const readyTileResponse = tileResponse;
setDashboard((current) =>
current?.state === "ready"
? {
...current,
document: applyDashboardTile(current.document, readyTileResponse),
}
: current,
);
tileSnapshotStore.saveReadyTile({
...snapshotContext,
response: readyTileResponse,
});
performanceMarks.markFirstTileReady();
return "ready";
}
function finishDashboardTileHydration(
key: string,
run: number,
signal: AbortSignal,
) {
if (!cancelled && !signal.aborted && run === hydrationRun) {
setHydratingItemIds((current) => {
const next = new Set(current);
next.delete(key);
return next;
});
}
}