diff --git a/web/ui/mantine-ui/src/api/api.ts b/web/ui/mantine-ui/src/api/api.ts index 9c8db2e3f5..163a1aae38 100644 --- a/web/ui/mantine-ui/src/api/api.ts +++ b/web/ui/mantine-ui/src/api/api.ts @@ -22,10 +22,12 @@ const createQueryFn = pathPrefix, path, params, + recordResponseTime, }: { pathPrefix: string; path: string; params?: Record; + recordResponseTime?: (time: number) => void; }) => async ({ signal }: { signal: AbortSignal }) => { const queryString = params @@ -33,6 +35,8 @@ const createQueryFn = : ""; try { + const startTime = Date.now(); + const res = await fetch( `${pathPrefix}/${API_PATH}${path}${queryString}`, { @@ -54,6 +58,10 @@ const createQueryFn = const apiRes = (await res.json()) as APIResponse; + if (recordResponseTime) { + recordResponseTime(Date.now() - startTime); + } + if (apiRes.status === "error") { throw new Error( apiRes.error !== undefined @@ -84,6 +92,7 @@ type QueryOptions = { path: string; params?: Record; enabled?: boolean; + recordResponseTime?: (time: number) => void; }; export const useAPIQuery = ({ @@ -91,6 +100,7 @@ export const useAPIQuery = ({ path, params, enabled, + recordResponseTime, }: QueryOptions) => { const { pathPrefix } = useSettings(); @@ -100,7 +110,7 @@ export const useAPIQuery = ({ refetchOnWindowFocus: false, gcTime: 0, enabled, - queryFn: createQueryFn({ pathPrefix, path, params }), + queryFn: createQueryFn({ pathPrefix, path, params, recordResponseTime }), }); }; diff --git a/web/ui/mantine-ui/src/pages/query/DataTable.tsx b/web/ui/mantine-ui/src/pages/query/DataTable.tsx index dfa045310c..4825ca1cc9 100644 --- a/web/ui/mantine-ui/src/pages/query/DataTable.tsx +++ b/web/ui/mantine-ui/src/pages/query/DataTable.tsx @@ -35,6 +35,9 @@ import HistogramChart from "./HistogramChart"; import { Histogram } from "../../types/types"; import { bucketRangeString } from "./HistogramHelpers"; import { useSettings } from "../../state/settingsSlice"; +import { useAppDispatch, useAppSelector } from "../../state/hooks"; +import { setVisualizer } from "../../state/queryPageSlice"; +import TimeInput from "./TimeInput"; dayjs.extend(timezone); const maxFormattableSeries = 1000; @@ -51,14 +54,21 @@ const limitSeries = ( }; export interface DataTableProps { - expr: string; - evalTime: number | null; + panelIdx: number; retriggerIdx: number; } -const DataTable: FC = ({ expr, evalTime, retriggerIdx }) => { +const DataTable: FC = ({ panelIdx, retriggerIdx }) => { const [scale, setScale] = useState("exponential"); const [limitResults, setLimitResults] = useState(true); + const [responseTime, setResponseTime] = useState(0); + + const { expr, visualizer } = useAppSelector( + (state) => state.queryPage.panels[panelIdx] + ); + const dispatch = useAppDispatch(); + + const { endTime, range } = visualizer; const { data, error, isFetching, isLoading, refetch } = useAPIQuery({ @@ -66,14 +76,15 @@ const DataTable: FC = ({ expr, evalTime, retriggerIdx }) => { path: "/query", params: { query: expr, - time: `${(evalTime !== null ? evalTime : Date.now()) / 1000}`, + time: `${(endTime !== null ? endTime : Date.now()) / 1000}`, }, enabled: expr !== "", + recordResponseTime: setResponseTime, }); useEffect(() => { expr !== "" && refetch(); - }, [retriggerIdx, refetch, expr, evalTime]); + }, [retriggerIdx, refetch, expr, endTime]); useLayoutEffect(() => { setLimitResults(true); @@ -121,130 +132,179 @@ const DataTable: FC = ({ expr, evalTime, retriggerIdx }) => { const doFormat = result.length <= maxFormattableSeries; return ( - <> - {limitResults && - ["vector", "matrix"].includes(resultType) && - result.length > maxDisplayableSeries && ( - } - title="Showing limited results" - > - Fetched {data.data.result.length} metrics, only displaying first{" "} - {maxDisplayableSeries} for performance reasons. - setLimitResults(false)}> - Show all results - - - )} - {!doFormat && ( - } - > - Showing more than {maxFormattableSeries} series, turning off label - formatting for performance reasons. + + {isLoading ? ( + + {Array.from(Array(5), (_, i) => ( + + ))} + + ) : data === undefined ? ( + No data queried yet + ) : result.length === 0 ? ( + }> + This query returned no data. - )} - - , - }} - styles={{ loader: { width: "100%", height: "100%" } }} - /> - - - {resultType === "vector" ? ( - limitSeries(result, limitResults).map((s, idx) => ( - - - - - - {s.value && s.value[1]} - {s.histogram && ( - - - - - - Count: {s.histogram[1].count} - - - Sum: {s.histogram[1].sum} - - - - x-axis scale: - - - - {histogramTable(s.histogram[1])} - - )} - - - )) - ) : resultType === "matrix" ? ( - limitSeries(result, limitResults).map((s, idx) => ( - - - - - - {s.values && - s.values.map((v, idx) => ( -
- {v[1]}{" "} - - @ {v[0]} - -
- ))} -
-
- )) - ) : resultType === "scalar" ? ( - - Scalar value - {result[1]} - - ) : resultType === "string" ? ( - - String value - {result[1]} - - ) : ( + ) : ( + <> + + + dispatch( + setVisualizer({ + idx: panelIdx, + visualizer: { ...visualizer, endTime: time }, + }) + ) + } + /> + + Load time: {responseTime}ms   Result series: {result.length} + + + + {limitResults && + ["vector", "matrix"].includes(resultType) && + result.length > maxDisplayableSeries && ( } + title="Showing limited results" > - Invalid result value type + Fetched {data.data.result.length} metrics, only displaying first{" "} + {maxDisplayableSeries} for performance reasons. + setLimitResults(false)}> + Show all results + )} -
-
-
- + + {!doFormat && ( + } + > + Showing more than {maxFormattableSeries} series, turning off label + formatting for performance reasons. + + )} + + + , + }} + styles={{ loader: { width: "100%", height: "100%" } }} + /> + + + + {resultType === "vector" ? ( + limitSeries(result, limitResults).map( + (s, idx) => ( + + + + + + {s.value && s.value[1]} + {s.histogram && ( + + + + + + Count:{" "} + {s.histogram[1].count} + + + Sum: {s.histogram[1].sum} + + + + x-axis scale: + + + + {histogramTable(s.histogram[1])} + + )} + + + ) + ) + ) : resultType === "matrix" ? ( + limitSeries(result, limitResults).map( + (s, idx) => ( + + + + + + {s.values && + s.values.map((v, idx) => ( +
+ {v[1]}{" "} + + @ {v[0]} + +
+ ))} +
+
+ ) + ) + ) : resultType === "scalar" ? ( + + Scalar value + + {result[1]} + + + ) : resultType === "string" ? ( + + String value + {result[1]} + + ) : ( + } + > + Invalid result value type + + )} +
+
+
+ + )} +
); }; diff --git a/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx b/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx index a73045e0b6..6e2814f128 100644 --- a/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx +++ b/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx @@ -84,26 +84,7 @@ const QueryPanel: FC = ({ idx, metricNames }) => { - - - dispatch( - setVisualizer({ - idx, - visualizer: { ...panel.visualizer, endTime: time }, - }) - ) - } - /> - - +