mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-03-11 15:34:56 +00:00
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:
parent
92f7fe306e
commit
ff6151fa49
12 changed files with 35 additions and 12 deletions
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "./static/css/main.ba692000.css",
|
"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",
|
"static/js/27.939f971b.chunk.js": "./static/js/27.939f971b.chunk.js",
|
||||||
"index.html": "./index.html"
|
"index.html": "./index.html"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/css/main.ba692000.css",
|
"static/css/main.ba692000.css",
|
||||||
"static/js/main.dd4b1276.js"
|
"static/js/main.c0e3dc67.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -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>
|
2
app/vmselect/vmui/static/js/main.c0e3dc67.js
Normal file
2
app/vmselect/vmui/static/js/main.c0e3dc67.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -12,13 +12,13 @@ import {ErrorTypes} from "../../../../types";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import usePrevious from "../../../../hooks/usePrevious";
|
import usePrevious from "../../../../hooks/usePrevious";
|
||||||
|
import {MAX_QUERY_FIELDS} from "../../../../config";
|
||||||
|
|
||||||
export interface QueryConfiguratorProps {
|
export interface QueryConfiguratorProps {
|
||||||
error?: ErrorTypes | string;
|
error?: ErrorTypes | string;
|
||||||
queryOptions: string[]
|
queryOptions: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MAX_QUERY_FIELDS = 4;
|
|
||||||
|
|
||||||
const QueryConfigurator: FC<QueryConfiguratorProps> = ({error, queryOptions}) => {
|
const QueryConfigurator: FC<QueryConfiguratorProps> = ({error, queryOptions}) => {
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ const CustomPanel: FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const {queryOptions} = useFetchQueryOptions();
|
const {queryOptions} = useFetchQueryOptions();
|
||||||
const {isLoading, liveData, graphData, error, traces} = useFetchQuery({
|
const {isLoading, liveData, graphData, error, warning, traces} = useFetchQuery({
|
||||||
visible: true,
|
visible: true,
|
||||||
customStep
|
customStep
|
||||||
});
|
});
|
||||||
|
@ -83,6 +83,7 @@ const CustomPanel: FC = () => {
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{error && <Alert color="error" severity="error" sx={{whiteSpace: "pre-wrap", mt: 2}}>{error}</Alert>}
|
{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") && <>
|
{graphData && period && (displayType === "chart") && <>
|
||||||
{isTracingEnabled && <TracingsView
|
{isTracingEnabled && <TracingsView
|
||||||
traces={tracesState}
|
traces={tracesState}
|
||||||
|
|
|
@ -46,7 +46,7 @@ const PredefinedPanels: FC<PredefinedPanelsProps> = ({
|
||||||
|
|
||||||
const validExpr = useMemo(() => Array.isArray(expr) && expr.every(q => q), [expr]);
|
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 : [],
|
predefinedQuery: validExpr ? expr : [],
|
||||||
display: "chart",
|
display: "chart",
|
||||||
visible,
|
visible,
|
||||||
|
@ -115,6 +115,7 @@ const PredefinedPanels: FC<PredefinedPanelsProps> = ({
|
||||||
<Box px={2} pb={2}>
|
<Box px={2} pb={2}>
|
||||||
{isLoading && <Spinner isLoading={true} height={"500px"}/>}
|
{isLoading && <Spinner isLoading={true} height={"500px"}/>}
|
||||||
{error && <Alert color="error" severity="error" sx={{whiteSpace: "pre-wrap", mt: 2}}>{error}</Alert>}
|
{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
|
{graphData && <GraphView
|
||||||
data={graphData}
|
data={graphData}
|
||||||
period={period}
|
period={period}
|
||||||
|
|
6
app/vmui/packages/vmui/src/config.tsx
Normal file
6
app/vmui/packages/vmui/src/config.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export const MAX_QUERY_FIELDS = 4;
|
||||||
|
export const MAX_SERIES = {
|
||||||
|
table: 100,
|
||||||
|
chart: 20,
|
||||||
|
code: Infinity,
|
||||||
|
};
|
|
@ -11,6 +11,7 @@ import {CustomStep} from "../state/graph/reducer";
|
||||||
import usePrevious from "./usePrevious";
|
import usePrevious from "./usePrevious";
|
||||||
import {arrayEquals} from "../utils/array";
|
import {arrayEquals} from "../utils/array";
|
||||||
import Trace from "../components/CustomPanel/Trace/Trace";
|
import Trace from "../components/CustomPanel/Trace/Trace";
|
||||||
|
import {MAX_SERIES} from "../config";
|
||||||
|
|
||||||
interface FetchQueryParams {
|
interface FetchQueryParams {
|
||||||
predefinedQuery?: string[]
|
predefinedQuery?: string[]
|
||||||
|
@ -28,6 +29,7 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
|
||||||
graphData?: MetricResult[],
|
graphData?: MetricResult[],
|
||||||
liveData?: InstantMetricResult[],
|
liveData?: InstantMetricResult[],
|
||||||
error?: ErrorTypes | string,
|
error?: ErrorTypes | string,
|
||||||
|
warning?: string,
|
||||||
traces?: Trace[],
|
traces?: Trace[],
|
||||||
} => {
|
} => {
|
||||||
const {query, displayType, serverUrl, time: {period}, queryControls: {nocache, isTracingEnabled}} = useAppState();
|
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 [liveData, setLiveData] = useState<InstantMetricResult[]>();
|
||||||
const [traces, setTraces] = useState<Trace[]>();
|
const [traces, setTraces] = useState<Trace[]>();
|
||||||
const [error, setError] = useState<ErrorTypes | string>();
|
const [error, setError] = useState<ErrorTypes | string>();
|
||||||
|
const [warning, setWarning] = useState<string>();
|
||||||
const [fetchQueue, setFetchQueue] = useState<AbortController[]>([]);
|
const [fetchQueue, setFetchQueue] = useState<AbortController[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -56,6 +59,7 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
|
||||||
const tempData: MetricBase[] = [];
|
const tempData: MetricBase[] = [];
|
||||||
const tempTraces: Trace[] = [];
|
const tempTraces: Trace[] = [];
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
|
|
||||||
for await (const response of responses) {
|
for await (const response of responses) {
|
||||||
const resp = await response.json();
|
const resp = await response.json();
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
@ -73,7 +77,14 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
|
||||||
setError(`${resp.errorType}\r\n${resp?.error}`);
|
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);
|
setTraces(tempTraces);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error && e.name !== "AbortError") {
|
if (e instanceof Error && e.name !== "AbortError") {
|
||||||
|
@ -107,9 +118,12 @@ export const useFetchQuery = ({predefinedQuery, visible, display, customStep}: F
|
||||||
[serverUrl, period, displayType, customStep]);
|
[serverUrl, period, displayType, customStep]);
|
||||||
|
|
||||||
const prevFetchUrl = usePrevious(fetchUrl);
|
const prevFetchUrl = usePrevious(fetchUrl);
|
||||||
|
const prevDisplayType = usePrevious(displayType);
|
||||||
|
|
||||||
useEffect(() => {
|
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);
|
setIsLoading(true);
|
||||||
const expr = predefinedQuery ?? query;
|
const expr = predefinedQuery ?? query;
|
||||||
throttledFetchData(fetchUrl, fetchQueue, (display || displayType), expr);
|
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));
|
setFetchQueue(fetchQueue.filter(f => !f.signal.aborted));
|
||||||
}, [fetchQueue]);
|
}, [fetchQueue]);
|
||||||
|
|
||||||
return {fetchUrl, isLoading, graphData, liveData, error, traces};
|
return {fetchUrl, isLoading, graphData, liveData, error, warning, traces};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import qs from "qs";
|
import qs from "qs";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import router from "../router";
|
import router from "../router";
|
||||||
import {MAX_QUERY_FIELDS} from "../components/CustomPanel/Configurator/Query/QueryConfigurator";
|
import {MAX_QUERY_FIELDS} from "../config";
|
||||||
|
|
||||||
const graphStateToUrlParams = {
|
const graphStateToUrlParams = {
|
||||||
"time.duration": "range_input",
|
"time.duration": "range_input",
|
||||||
|
|
|
@ -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).
|
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: [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: [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)!
|
* 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)!
|
||||||
|
|
Loading…
Reference in a new issue