From 787b1b3367ee6ae496ae6af48b71bc864643c952 Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Mon, 11 Aug 2025 13:31:28 +0200 Subject: [PATCH] Measure alert filtering time Signed-off-by: Julius Volz --- web/ui/mantine-ui/src/pages/AlertsPage.tsx | 34 +++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/web/ui/mantine-ui/src/pages/AlertsPage.tsx b/web/ui/mantine-ui/src/pages/AlertsPage.tsx index 00e6595fd4..5498de5796 100644 --- a/web/ui/mantine-ui/src/pages/AlertsPage.tsx +++ b/web/ui/mantine-ui/src/pages/AlertsPage.tsx @@ -20,7 +20,7 @@ import badgeClasses from "../Badge.module.css"; import panelClasses from "../Panel.module.css"; import RuleDefinition from "../components/RuleDefinition"; import { humanizeDurationRelative, now } from "../lib/formatTime"; -import { Fragment, useEffect, useMemo } from "react"; +import { Fragment, useEffect, useMemo, useRef } from "react"; import { StateMultiSelect } from "../components/StateMultiSelect"; import { IconInfoCircle, IconSearch } from "@tabler/icons-react"; import { LabelBadges } from "../components/LabelBadges"; @@ -164,6 +164,38 @@ export default function AlertsPage() { withDefault(StringParam, "") ); const [debouncedSearch] = useDebouncedValue(searchFilter.trim(), 250); + + // Measure how long it takes to render the filtered list after debouncedSearch changes. + const searchRenderStartRef = useRef(null); + const prevDebouncedSearchRef = useRef(debouncedSearch); + if (prevDebouncedSearchRef.current !== debouncedSearch) { + searchRenderStartRef.current = performance.now(); + prevDebouncedSearchRef.current = debouncedSearch; + } + + // After commit, wait for paint to complete, then log duration. + useEffect(() => { + if (searchRenderStartRef.current != null) { + const start = searchRenderStartRef.current; + let raf1 = 0; + let raf2 = 0; + raf1 = requestAnimationFrame(() => { + raf2 = requestAnimationFrame(() => { + const duration = performance.now() - start; + console.log( + `AlertsPage: filtered list render time ${duration.toFixed(2)} ms (search="${debouncedSearch}")` + ); + searchRenderStartRef.current = null; + }); + }); + return () => { + cancelAnimationFrame(raf1); + cancelAnimationFrame(raf2); + }; + } + return; + }); + const [showEmptyGroups, setShowEmptyGroups] = useLocalStorage({ key: "alertsPage.showEmptyGroups", defaultValue: false,