vmui: limit number of plotted series (#3229)

* feat: add maximum display series by tabs

* feat: add warning on PredefinedPanels.tsx

* docs/CHANGELOG.md: vmui limit number of plotted series

* docs/CHANGELOG.md: vmui limit number of plotted series

* wip

Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
Yury Molodov 2022-10-13 11:13:47 +02:00 committed by Aliaksandr Valialkin
parent 07140e0877
commit a50f2e141c
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
12 changed files with 35 additions and 12 deletions

View file

@ -1,12 +1,12 @@
{
"files": {
"main.css": "./static/css/main.ba692000.css",
"main.js": "./static/js/main.dd4b1276.js",
"main.js": "./static/js/main.c0e3dc67.js",
"static/js/27.939f971b.chunk.js": "./static/js/27.939f971b.chunk.js",
"index.html": "./index.html"
},
"entrypoints": [
"static/css/main.ba692000.css",
"static/js/main.dd4b1276.js"
"static/js/main.c0e3dc67.js"
]
}

View file

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.dd4b1276.js"></script><link href="./static/css/main.ba692000.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.c0e3dc67.js"></script><link href="./static/css/main.ba692000.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -12,13 +12,13 @@ import {ErrorTypes} from "../../../../types";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import usePrevious from "../../../../hooks/usePrevious";
import {MAX_QUERY_FIELDS} from "../../../../config";
export interface QueryConfiguratorProps {
error?: ErrorTypes | string;
queryOptions: string[]
}
export const MAX_QUERY_FIELDS = 4;
const QueryConfigurator: FC<QueryConfiguratorProps> = ({error, queryOptions}) => {

View file

@ -40,7 +40,7 @@ const CustomPanel: FC = () => {
};
const {queryOptions} = useFetchQueryOptions();
const {isLoading, liveData, graphData, error, traces} = useFetchQuery({
const {isLoading, liveData, graphData, error, warning, traces} = useFetchQuery({
visible: true,
customStep
});
@ -83,6 +83,7 @@ const CustomPanel: FC = () => {
</Box>
</Box>
{error && <Alert color="error" severity="error" sx={{whiteSpace: "pre-wrap", mt: 2}}>{error}</Alert>}
{warning && <Alert color="warning" severity="warning" sx={{whiteSpace: "pre-wrap", my: 2}}>{warning}</Alert>}
{graphData && period && (displayType === "chart") && <>
{isTracingEnabled && <TracingsView
traces={tracesState}

View file

@ -46,7 +46,7 @@ const PredefinedPanels: FC<PredefinedPanelsProps> = ({
const validExpr = useMemo(() => Array.isArray(expr) && expr.every(q => q), [expr]);
const {isLoading, graphData, error} = useFetchQuery({
const {isLoading, graphData, error, warning} = useFetchQuery({
predefinedQuery: validExpr ? expr : [],
display: "chart",
visible,
@ -115,6 +115,7 @@ const PredefinedPanels: FC<PredefinedPanelsProps> = ({
<Box px={2} pb={2}>
{isLoading && <Spinner isLoading={true} height={"500px"}/>}
{error && <Alert color="error" severity="error" sx={{whiteSpace: "pre-wrap", mt: 2}}>{error}</Alert>}
{warning && <Alert color="warning" severity="warning" sx={{whiteSpace: "pre-wrap", my: 2}}>{warning}</Alert>}
{graphData && <GraphView
data={graphData}
period={period}

View file

@ -0,0 +1,6 @@
export const MAX_QUERY_FIELDS = 4;
export const MAX_SERIES = {
table: 100,
chart: 20,
code: Infinity,
};

View file

@ -11,6 +11,7 @@ import {CustomStep} from "../state/graph/reducer";
import usePrevious from "./usePrevious";
import {arrayEquals} from "../utils/array";
import Trace from "../components/CustomPanel/Trace/Trace";
import {MAX_SERIES} from "../config";
interface FetchQueryParams {
predefinedQuery?: string[]
@ -28,6 +29,7 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
graphData?: MetricResult[],
liveData?: InstantMetricResult[],
error?: ErrorTypes | string,
warning?: string,
traces?: Trace[],
} => {
const {query, displayType, serverUrl, time: {period}, queryControls: {nocache, isTracingEnabled}} = useAppState();
@ -37,6 +39,7 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
const [liveData, setLiveData] = useState<InstantMetricResult[]>();
const [traces, setTraces] = useState<Trace[]>();
const [error, setError] = useState<ErrorTypes | string>();
const [warning, setWarning] = useState<string>();
const [fetchQueue, setFetchQueue] = useState<AbortController[]>([]);
useEffect(() => {
@ -56,6 +59,7 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
const tempData: MetricBase[] = [];
const tempTraces: Trace[] = [];
let counter = 1;
for await (const response of responses) {
const resp = await response.json();
if (response.ok) {
@ -73,7 +77,14 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
setError(`${resp.errorType}\r\n${resp?.error}`);
}
}
isDisplayChart ? setGraphData(tempData as MetricResult[]) : setLiveData(tempData as InstantMetricResult[]);
const length = tempData.length;
const seriesLimit = MAX_SERIES[displayType];
const result = tempData.slice(0, seriesLimit);
const limitText = `Showing ${seriesLimit} series out of ${length} series due to performance reasons. Please narrow down the query, so it returns less series`;
setWarning(length > seriesLimit ? limitText : "");
isDisplayChart ? setGraphData(result as MetricResult[]) : setLiveData(result as InstantMetricResult[]);
setTraces(tempTraces);
} catch (e) {
if (e instanceof Error && e.name !== "AbortError") {
@ -107,9 +118,12 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
[serverUrl, period, displayType, customStep]);
const prevFetchUrl = usePrevious(fetchUrl);
const prevDisplayType = usePrevious(displayType);
useEffect(() => {
if (!visible || (fetchUrl && prevFetchUrl && arrayEquals(fetchUrl, prevFetchUrl)) || !fetchUrl?.length) return;
const equalFetchUrl = fetchUrl && prevFetchUrl && arrayEquals(fetchUrl, prevFetchUrl);
const changedDisplayType = displayType !== prevDisplayType;
if (!visible || (equalFetchUrl && !changedDisplayType) || !fetchUrl?.length) return;
setIsLoading(true);
const expr = predefinedQuery ?? query;
throttledFetchData(fetchUrl, fetchQueue, (display || displayType), expr);
@ -122,5 +136,5 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
setFetchQueue(fetchQueue.filter(f => !f.signal.aborted));
}, [fetchQueue]);
return {fetchUrl, isLoading, graphData, liveData, error, traces};
return {fetchUrl, isLoading, graphData, liveData, error, warning, traces};
};

View file

@ -1,7 +1,7 @@
import qs from "qs";
import get from "lodash.get";
import router from "../router";
import {MAX_QUERY_FIELDS} from "../components/CustomPanel/Configurator/Query/QueryConfigurator";
import {MAX_QUERY_FIELDS} from "../config";
const graphStateToUrlParams = {
"time.duration": "range_input",

View file

@ -38,6 +38,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
See [the corresponding issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3208).
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): allow controlling staleness tracking on a per-[scrape_config](https://docs.victoriametrics.com/sd_configs.html#scrape_configs) basis by specifying `no_stale_markers: true` or `no_stale_markers: false` option in the corresponding [scrape_config](https://docs.victoriametrics.com/sd_configs.html#scrape_configs).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): limit the number of plotted series. This should prevent from browser crashes or hands when the query returns big number of time series. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3155).
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): automatically update graph, legend and url after the removal of query field. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3169) and [this comment](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3196#issuecomment-1269765205).
* BUGFIX: [vmalert](https://docs.victoriametrics.com/vmalert.html): remove duplicate `alertname` JSON entry from generated alerts. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3053). Thanks to @Howie59 for [the fix](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3182)!