perf(web): add dashboard hydration observability #50

Merged
vince merged 2 commits from codex/dashboard-observability into main 2026-06-20 15:06:55 +02:00
2 changed files with 119 additions and 10 deletions
Showing only changes of commit 0d27920a78 - Show all commits

View file

@ -389,6 +389,62 @@ describe("dashboard API route", () => {
]);
});
test("logs coalesced metadata when shared tile requests fail", async () => {
const cache = createDashboardTileCache();
let rejectLoad: ((error: Error) => void) | undefined;
const load = vi.fn(() =>
new Promise<never>((_resolve, reject) => {
rejectLoad = reject;
})
);
const first = cache.resolve("tile-a", 30_000, 1_000, load);
const second = cache.resolve("tile-a", 30_000, 1_000, load);
await Promise.resolve();
expect(load).toHaveBeenCalledTimes(1);
rejectLoad?.(new TypeError("upstream failed"));
await expect(first).rejects.toThrow("upstream failed");
await expect(second).rejects.toMatchObject({
cache: "miss",
coalesced: true,
cause: expect.any(TypeError),
});
});
test("logs coalesced metadata for failed tile cache resolution", async () => {
const logs: DashboardTileResolutionLogEvent[] = [];
const tile = { kind: "service", groupId: "essentials", id: "vaultwarden" } as const;
await expect(
loadDashboardTileResponse(tile, {
logTileResolution: (event) => logs.push(event),
refreshSeedDocument: true,
seedIfEmpty: true,
tileCache: {
async resolve() {
throw {
cache: "miss",
cause: new TypeError("coalesced cache failed"),
coalesced: true,
};
},
},
}),
).rejects.toThrow("coalesced cache failed");
expect(logs).toEqual([
expect.objectContaining({
cache: "miss",
coalesced: true,
errorCategory: "TypeError",
status: "error",
tileKey: dashboardTileCacheKey(tile),
}),
]);
});
test("logs error categories for failed tile cache resolution", async () => {
const logs: DashboardTileResolutionLogEvent[] = [];
const tile = { kind: "service", groupId: "essentials", id: "vaultwarden" } as const;

View file

@ -77,11 +77,18 @@ export function createDashboardTileCache(): DashboardTileCache {
const active = inFlight.get(key);
if (active) {
return active.then((response) => ({
cache: "miss",
return active
.then((response) => ({
cache: "miss" as const,
coalesced: true,
response,
}));
}))
.catch((error) => {
throw new DashboardTileCacheResolutionError(error, {
cache: "miss",
coalesced: true,
});
});
}
const request = load()
@ -101,7 +108,7 @@ export function createDashboardTileCache(): DashboardTileCache {
inFlight.set(key, request);
return request.then((response) => ({
cache: "miss",
cache: "miss" as const,
coalesced: false,
response,
}));
@ -216,15 +223,16 @@ export async function loadDashboardTileResponse(
});
return result.response;
} catch (error) {
const cacheError = dashboardTileCacheResolutionError(error);
logDashboardTileResolution(options, {
cache: "miss",
coalesced: false,
cache: cacheError?.cache ?? "miss",
coalesced: cacheError?.coalesced ?? false,
durationMs: elapsedDashboardTileMs(startedAt, options),
errorCategory: dashboardTileErrorCategory(error),
errorCategory: dashboardTileErrorCategory(cacheError?.cause ?? error),
status: "error",
tileKey,
});
throw error;
throw cacheError?.cause ?? error;
}
}
@ -288,6 +296,51 @@ function dashboardTileErrorCategory(error: unknown): string {
return "unknown";
}
class DashboardTileCacheResolutionError extends Error {
readonly cache: "hit" | "miss";
readonly coalesced: boolean;
override readonly cause: unknown;
constructor(
cause: unknown,
metadata: Pick<DashboardTileCacheResult, "cache" | "coalesced">,
) {
super("Dashboard tile cache resolution failed");
this.name = "DashboardTileCacheResolutionError";
this.cause = cause;
this.cache = metadata.cache;
this.coalesced = metadata.coalesced;
}
}
function dashboardTileCacheResolutionError(
error: unknown,
): DashboardTileCacheResolutionFailure | null {
if (
typeof error === "object" &&
error !== null &&
"cache" in error &&
(error.cache === "hit" || error.cache === "miss") &&
"coalesced" in error &&
typeof error.coalesced === "boolean" &&
"cause" in error
) {
return {
cache: error.cache,
cause: error.cause,
coalesced: error.coalesced,
};
}
return null;
}
interface DashboardTileCacheResolutionFailure {
cache: "hit" | "miss";
cause: unknown;
coalesced: boolean;
}
function dashboardTileTtlMs(
document: DashboardDocument,
tile: DashboardTileReference,