From 0b9c4d63751a7043de22a2beb324a0813279eda4 Mon Sep 17 00:00:00 2001 From: ajnart Date: Wed, 29 Jul 2026 06:50:13 +0200 Subject: [PATCH] fix: address performance review feedback --- .../src/app/[locale]/widgets/[kind]/page.tsx | 13 +++++++------ .../integration-multi-select-grid.tsx | 2 +- .../integration/integration-select-grid.tsx | 2 +- packages/db/proxy-reader.spec.ts | 17 +++++++++++++++++ packages/db/proxy-reader.ts | 7 ++++++- pnpm-workspace.yaml | 1 - scripts/benchmarks/dev-benchmark-lib.mts | 8 ++++++-- scripts/benchmarks/dev-benchmark-lib.spec.ts | 4 ++++ 8 files changed, 42 insertions(+), 12 deletions(-) diff --git a/apps/nextjs/src/app/[locale]/widgets/[kind]/page.tsx b/apps/nextjs/src/app/[locale]/widgets/[kind]/page.tsx index 56ff3e215..00d54fc1a 100644 --- a/apps/nextjs/src/app/[locale]/widgets/[kind]/page.tsx +++ b/apps/nextjs/src/app/[locale]/widgets/[kind]/page.tsx @@ -1,7 +1,7 @@ +import { Suspense } from "react"; import { notFound } from "next/navigation"; -import { Center } from "@mantine/core"; +import { Center, Loader } from "@mantine/core"; -import { env } from "@homarr/common/env"; import { db } from "@homarr/db"; import type { WidgetKind } from "@homarr/definitions"; import { widgetKinds } from "@homarr/widgets/manifest"; @@ -13,7 +13,8 @@ interface Props { } export default async function WidgetPreview(props: Props) { - if (!(widgetKinds.includes((await props.params).kind as WidgetKind) || env.NODE_ENV !== "development")) { + const { kind } = await props.params; + if (!widgetKinds.includes(kind as WidgetKind)) { notFound(); } @@ -26,11 +27,11 @@ export default async function WidgetPreview(props: Props) { }, }); - const sort = (await props.params).kind as WidgetKind; - return (
- + }> + +
); } diff --git a/apps/nextjs/src/components/integration/integration-multi-select-grid.tsx b/apps/nextjs/src/components/integration/integration-multi-select-grid.tsx index ff1bd4ff1..44e947557 100644 --- a/apps/nextjs/src/components/integration/integration-multi-select-grid.tsx +++ b/apps/nextjs/src/components/integration/integration-multi-select-grid.tsx @@ -136,7 +136,7 @@ export const IntegrationMultiSelectGrid = ({ ); })} - {filtered.length === 0 && ( + {!isPending && filtered.length === 0 && (
{t("common.noResults")}
diff --git a/apps/nextjs/src/components/integration/integration-select-grid.tsx b/apps/nextjs/src/components/integration/integration-select-grid.tsx index 5127534d9..017f0acf5 100644 --- a/apps/nextjs/src/components/integration/integration-select-grid.tsx +++ b/apps/nextjs/src/components/integration/integration-select-grid.tsx @@ -89,7 +89,7 @@ export const IntegrationSelectGrid = ({ onSelect, enableMockIntegration = false ))} - {filtered.length === 0 && ( + {!isPending && filtered.length === 0 && (
{t("common.noResults")}
diff --git a/packages/db/proxy-reader.spec.ts b/packages/db/proxy-reader.spec.ts index 16fa37d9a..f46c79cef 100644 --- a/packages/db/proxy-reader.spec.ts +++ b/packages/db/proxy-reader.spec.ts @@ -44,6 +44,7 @@ describe("proxy database pools", () => { vi.resetModules(); vi.clearAllMocks(); for (const key of Object.keys(mocks.dbEnv)) delete mocks.dbEnv[key]; + mocks.mysqlCreatePool.mockReturnValue({}); }); it("bounds MySQL URL pools instead of falling back to the driver defaults", async () => { @@ -108,4 +109,20 @@ describe("proxy database pools", () => { expect(mocks.postgresPool).toHaveBeenCalledWith(expect.objectContaining({ max: 1 })); }); + + it("retries initialization after a transient driver failure", async () => { + Object.assign(mocks.dbEnv, { + DRIVER: "mysql2", + URL: "mysql://homarr:test@localhost:3306/homarr", + }); + mocks.mysqlCreatePool.mockImplementationOnce(() => { + throw new Error("database unavailable"); + }); + + const { getOnboardingStepForProxyAsync } = await import("./proxy-reader"); + + await expect(getOnboardingStepForProxyAsync()).rejects.toThrow("database unavailable"); + await expect(getOnboardingStepForProxyAsync()).resolves.toBe("finish"); + expect(mocks.mysqlCreatePool).toHaveBeenCalledTimes(2); + }); }); diff --git a/packages/db/proxy-reader.ts b/packages/db/proxy-reader.ts index d0bb98319..1fd85f223 100644 --- a/packages/db/proxy-reader.ts +++ b/packages/db/proxy-reader.ts @@ -152,7 +152,12 @@ const createProxyReaderAsync = async (): Promise => { let proxyReaderPromise: Promise | null = null; const getProxyReaderAsync = () => { - proxyReaderPromise ??= createProxyReaderAsync(); + if (!proxyReaderPromise) { + proxyReaderPromise = createProxyReaderAsync().catch((error: unknown) => { + proxyReaderPromise = null; + throw error; + }); + } return proxyReaderPromise; }; diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 15439d57b..49d8f0dc8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -33,7 +33,6 @@ catalog: "@mantine/notifications": ^9.4.1 "@mantine/spotlight": ^9.4.1 "@mantine/tiptap": ^9.4.1 - "@next/eslint-plugin-next": 16.2.10 "@octokit/auth-app": ^8.2.0 "@paralleldrive/cuid2": ^3.3.0 "@playwright/test": ^1.61.1 diff --git a/scripts/benchmarks/dev-benchmark-lib.mts b/scripts/benchmarks/dev-benchmark-lib.mts index ee20502a3..858e8fe7d 100644 --- a/scripts/benchmarks/dev-benchmark-lib.mts +++ b/scripts/benchmarks/dev-benchmark-lib.mts @@ -30,7 +30,11 @@ export const summarize = (numbers: Array): NumericSum return { count: 0, medianMs: null, p95Ms: null, standardDeviationMs: null, outliersMs: [] }; } - const medianIndex = Math.floor(sorted.length / 2); + const middleIndex = Math.floor(sorted.length / 2); + let medianMs = sorted[middleIndex] ?? 0; + if (sorted.length % 2 === 0) { + medianMs = ((sorted[middleIndex - 1] ?? 0) + medianMs) / 2; + } const p95Index = Math.min(sorted.length - 1, Math.ceil(sorted.length * 0.95) - 1); const mean = sorted.reduce((total, value) => total + value, 0) / sorted.length; const variance = sorted.reduce((total, value) => total + (value - mean) ** 2, 0) / sorted.length; @@ -41,7 +45,7 @@ export const summarize = (numbers: Array): NumericSum return { count: sorted.length, - medianMs: sorted[medianIndex] ?? 0, + medianMs, p95Ms: sorted[p95Index] ?? 0, standardDeviationMs: Math.round(Math.sqrt(variance)), outliersMs: sorted.filter((value) => value > outlierLimit), diff --git a/scripts/benchmarks/dev-benchmark-lib.spec.ts b/scripts/benchmarks/dev-benchmark-lib.spec.ts index f1e113376..fa374270c 100644 --- a/scripts/benchmarks/dev-benchmark-lib.spec.ts +++ b/scripts/benchmarks/dev-benchmark-lib.spec.ts @@ -26,6 +26,10 @@ describe("development benchmark measurements", () => { }); }); + it("averages both middle values for an even sample count", () => { + expect(summarize([100, 200]).medianMs).toBe(150); + }); + it("requires a successful authenticated board hydration and interaction", () => { expect( validateBrowserMeasurement({