Graph range vector selectors as instant vector selectors with notice

Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
Julius Volz 2024-09-08 21:11:06 +02:00
parent 1f1ca37fd7
commit d23872ef30
2 changed files with 59 additions and 26 deletions

View file

@ -1,5 +1,5 @@
import { FC, useEffect, useId, useState } from "react"; import { FC, useEffect, useId, useState } from "react";
import { Alert, Skeleton, Box, LoadingOverlay } from "@mantine/core"; import { Alert, Skeleton, Box, LoadingOverlay, Stack } from "@mantine/core";
import { IconAlertTriangle, IconInfoCircle } from "@tabler/icons-react"; import { IconAlertTriangle, IconInfoCircle } from "@tabler/icons-react";
import { RangeQueryResult } from "../../api/responseTypes/query"; import { RangeQueryResult } from "../../api/responseTypes/query";
import { SuccessAPIResponse, useAPIQuery } from "../../api/api"; import { SuccessAPIResponse, useAPIQuery } from "../../api/api";
@ -13,9 +13,12 @@ import "uplot/dist/uPlot.min.css";
import "./uplot.css"; import "./uplot.css";
import { useElementSize } from "@mantine/hooks"; import { useElementSize } from "@mantine/hooks";
import UPlotChart, { UPlotChartRange } from "./UPlotChart"; import UPlotChart, { UPlotChartRange } from "./UPlotChart";
import ASTNode, { nodeType } from "../../promql/ast";
import serializeNode from "../../promql/serialize";
export interface GraphProps { export interface GraphProps {
expr: string; expr: string;
node: ASTNode | null;
endTime: number | null; endTime: number | null;
range: number; range: number;
resolution: GraphResolution; resolution: GraphResolution;
@ -27,6 +30,7 @@ export interface GraphProps {
const Graph: FC<GraphProps> = ({ const Graph: FC<GraphProps> = ({
expr, expr,
node,
endTime, endTime,
range, range,
resolution, resolution,
@ -38,6 +42,22 @@ const Graph: FC<GraphProps> = ({
const { ref, width } = useElementSize(); const { ref, width } = useElementSize();
const [rerender, setRerender] = useState(true); const [rerender, setRerender] = useState(true);
const effectiveExpr =
node === null
? expr
: serializeNode(
node.type === nodeType.matrixSelector
? {
type: nodeType.vectorSelector,
name: node.name,
matchers: node.matchers,
offset: node.offset,
timestamp: node.timestamp,
startOrEnd: node.startOrEnd,
}
: node
);
const effectiveEndTime = (endTime !== null ? endTime : Date.now()) / 1000; const effectiveEndTime = (endTime !== null ? endTime : Date.now()) / 1000;
const startTime = effectiveEndTime - range / 1000; const startTime = effectiveEndTime - range / 1000;
const effectiveResolution = getEffectiveResolution(resolution, range) / 1000; const effectiveResolution = getEffectiveResolution(resolution, range) / 1000;
@ -47,12 +67,12 @@ const Graph: FC<GraphProps> = ({
key: [useId()], key: [useId()],
path: "/query_range", path: "/query_range",
params: { params: {
query: expr, query: effectiveExpr,
step: effectiveResolution.toString(), step: effectiveResolution.toString(),
start: startTime.toString(), start: startTime.toString(),
end: effectiveEndTime.toString(), end: effectiveEndTime.toString(),
}, },
enabled: expr !== "", enabled: effectiveExpr !== "",
}); });
// Bundle the chart data and the displayed range together. This has two purposes: // Bundle the chart data and the displayed range together. This has two purposes:
@ -82,8 +102,8 @@ const Graph: FC<GraphProps> = ({
// Re-execute the query when the user presses Enter (or hits the Execute button). // Re-execute the query when the user presses Enter (or hits the Execute button).
useEffect(() => { useEffect(() => {
expr !== "" && refetch(); effectiveExpr !== "" && refetch();
}, [retriggerIdx, refetch, expr, endTime, range, resolution]); }, [retriggerIdx, refetch, effectiveExpr, endTime, range, resolution]);
// The useElementSize hook above only gets a valid size on the second render, so this // The useElementSize hook above only gets a valid size on the second render, so this
// is a workaround to make the component render twice after mount. // is a workaround to make the component render twice after mount.
@ -133,27 +153,39 @@ const Graph: FC<GraphProps> = ({
} }
return ( return (
<Box pos="relative" ref={ref} className={classes.chartWrapper}> <Stack>
<LoadingOverlay {node !== null && node.type === nodeType.matrixSelector && (
visible={isFetching} <Alert
zIndex={1000} color="orange"
h={570} title="Graphing modified expression"
overlayProps={{ radius: "sm", blur: 0.5 }} icon={<IconAlertTriangle size={14} />}
loaderProps={{ type: "dots", color: "gray.6" }} >
// loaderProps={{ <strong>Note:</strong> Range vector selectors can't be graphed, so
// children: <Skeleton m={0} w="100%" h="100%" />, graphing the equivalent instant vector selector instead.
// }} </Alert>
// styles={{ loader: { width: "100%", height: "100%" } }} )}
/> <Box pos="relative" ref={ref} className={classes.chartWrapper}>
<UPlotChart <LoadingOverlay
data={dataAndRange.data.data.result} visible={isFetching}
range={dataAndRange.range} zIndex={1000}
width={width} h={570}
showExemplars={showExemplars} overlayProps={{ radius: "sm", blur: 0.5 }}
displayMode={displayMode} loaderProps={{ type: "dots", color: "gray.6" }}
onSelectRange={onSelectRange} // loaderProps={{
/> // children: <Skeleton m={0} w="100%" h="100%" />,
</Box> // }}
// styles={{ loader: { width: "100%", height: "100%" } }}
/>
<UPlotChart
data={dataAndRange.data.data.result}
range={dataAndRange.range}
width={width}
showExemplars={showExemplars}
displayMode={displayMode}
onSelectRange={onSelectRange}
/>
</Box>
</Stack>
); );
}; };

View file

@ -296,6 +296,7 @@ const QueryPanel: FC<PanelProps> = ({ idx, metricNames }) => {
<Space h="lg" /> <Space h="lg" />
<Graph <Graph
expr={expr} expr={expr}
node={selectedNode?.node ?? null}
endTime={panel.visualizer.endTime} endTime={panel.visualizer.endTime}
range={panel.visualizer.range} range={panel.visualizer.range}
resolution={panel.visualizer.resolution} resolution={panel.visualizer.resolution}