fix: optimize monitor data fetching for large time ranges in overview detail (#10)
Co-authored-by: yaojiping <yaojiping@infini.ltd>
This commit is contained in:
parent
7dd3808942
commit
eedcda0fec
|
@ -48,11 +48,8 @@ export default (props) => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className={styles.lineWrapper}>
|
|
||||||
<div className={styles.chartTitle}>{props.title}</div>
|
|
||||||
<div className={styles.chartBody}>
|
<div className={styles.chartBody}>
|
||||||
<Line {...config} />
|
<Line {...config} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,13 +3,32 @@ import { Line } from "@ant-design/plots";
|
||||||
import styles from "./index.scss";
|
import styles from "./index.scss";
|
||||||
import MetricLine from "./MetricLine";
|
import MetricLine from "./MetricLine";
|
||||||
import { formatMessage } from "umi/locale";
|
import { formatMessage } from "umi/locale";
|
||||||
|
import { useState } from "react";
|
||||||
|
import request from "@/utils/request";
|
||||||
|
import MetricChart from "@/pages/Platform/Overview/components/MetricChart";
|
||||||
|
|
||||||
export default ({ metrics = {}, renderExtraMetric }) => {
|
export default (props) => {
|
||||||
let newMetrics = Object.values(metrics).map((item) => {
|
const { action, timeRange, timezone, timeout, overview, renderExtraMetric, metrics = [], queryParams } = props
|
||||||
let key = item.key;
|
|
||||||
|
return (
|
||||||
|
<div className={styles.metricChart}>
|
||||||
|
{metrics.map((metricKey) => {
|
||||||
|
return (
|
||||||
|
<MetricChart
|
||||||
|
key={metricKey}
|
||||||
|
timezone={timezone}
|
||||||
|
timeRange={timeRange}
|
||||||
|
fetchUrl={action}
|
||||||
|
metricKey={metricKey}
|
||||||
|
title={formatMessage({id: "cluster.metrics.axis." + metricKey + ".title"})}
|
||||||
|
queryParams={queryParams}
|
||||||
|
timeout={timeout}
|
||||||
|
className={styles.lineWrapper}
|
||||||
|
formatMetric={(metric) => {
|
||||||
|
if (!metric) return metric;
|
||||||
let units = "";
|
let units = "";
|
||||||
let newData = [];
|
const newData = [];
|
||||||
item.lines.map((line) => {
|
(metric.lines || []).map((line) => {
|
||||||
let category = line.metric.label;
|
let category = line.metric.label;
|
||||||
if (!units) {
|
if (!units) {
|
||||||
if (line.metric.formatType.toLowerCase() == "bytes") {
|
if (line.metric.formatType.toLowerCase() == "bytes") {
|
||||||
|
@ -27,27 +46,21 @@ export default ({ metrics = {}, renderExtraMetric }) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return { key: key, data: newData, units: units, order: item.order };
|
return { ...metric, data: newData, units };
|
||||||
});
|
}}
|
||||||
newMetrics = newMetrics.sort((a, b) => a.order - b.order);
|
customRenderChart={(metric) => {
|
||||||
|
if (!metric) return null
|
||||||
return (
|
const config = {
|
||||||
<div className={styles.metricChart}>
|
data: metric.data,
|
||||||
{newMetrics.map((item) => {
|
|
||||||
if(["node_health", "parent_breaker", "index_health"].includes(item.key)){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let config = {
|
|
||||||
data: item.data,
|
|
||||||
xField: "x",
|
xField: "x",
|
||||||
yField: "y",
|
yField: "y",
|
||||||
seriesField: "category",
|
seriesField: "category",
|
||||||
title: `${formatMessage({
|
yUnits: metric.units,
|
||||||
id: "cluster.metrics.axis." + item.key + ".title",
|
|
||||||
})} ${item.units ? "(" + item.units + ")" : ""}`,
|
|
||||||
yUnits: item.units,
|
|
||||||
};
|
};
|
||||||
return <MetricLine {...config} key={item.key} />;
|
return <MetricLine {...config} key={metric.key} />
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
})}
|
})}
|
||||||
{renderExtraMetric ? renderExtraMetric() : null}
|
{renderExtraMetric ? renderExtraMetric() : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,38 +1,21 @@
|
||||||
import React, { useMemo, useEffect } from "react";
|
import React, { useMemo, useEffect } from "react";
|
||||||
import { ESPrefix } from "@/services/common";
|
|
||||||
import useFetch from "@/lib/hooks/use_fetch";
|
|
||||||
import { calculateBounds } from "@/components/vendor/data/common/query/timefilter";
|
|
||||||
import MetricLineList from "./MetricLineList";
|
import MetricLineList from "./MetricLineList";
|
||||||
|
import { formatTimeRange } from "@/lib/elasticsearch/util";
|
||||||
|
|
||||||
export default ({ action, timeRange, overview, setSpinning, renderExtraMetric }) => {
|
export default (props) => {
|
||||||
|
|
||||||
|
const { action, bucketSize, timeRange, overview, setSpinning, renderExtraMetric, metrics = [] } = props
|
||||||
|
|
||||||
const queryParams = useMemo(() => {
|
const queryParams = useMemo(() => {
|
||||||
const bounds = calculateBounds({
|
const newParams = formatTimeRange(timeRange);
|
||||||
from: timeRange.min,
|
if (overview) {
|
||||||
to: timeRange.max,
|
newParams.overview = overview;
|
||||||
});
|
}
|
||||||
let params = {
|
if (bucketSize) {
|
||||||
min: bounds.min.valueOf(),
|
newParams.bucket_size = bucketSize
|
||||||
max: bounds.max.valueOf(),
|
}
|
||||||
};
|
return newParams;
|
||||||
params.overview = overview;
|
}, [timeRange, bucketSize]);
|
||||||
return params;
|
|
||||||
}, [timeRange]);
|
|
||||||
|
|
||||||
const { loading, error, value } = useFetch(
|
return <MetricLineList {...props} queryParams={queryParams} />;
|
||||||
action,
|
|
||||||
{ queryParams },
|
|
||||||
[action, queryParams]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setSpinning(loading);
|
|
||||||
}, [loading]);
|
|
||||||
|
|
||||||
const metrics = useMemo(() => {
|
|
||||||
const { metrics = {} } = value || {};
|
|
||||||
return metrics;
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
return <MetricLineList metrics={metrics} renderExtraMetric={renderExtraMetric}/>;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { formatMessage } from "umi/locale";
|
||||||
import DatePicker from "@/common/src/DatePicker";
|
import DatePicker from "@/common/src/DatePicker";
|
||||||
import { getLocale } from "umi/locale";
|
import { getLocale } from "umi/locale";
|
||||||
import { getTimezone } from "@/utils/utils";
|
import { getTimezone } from "@/utils/utils";
|
||||||
|
import { TIMEOUT_CACHE_KEY } from "../../Monitor";
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ export default (props) => {
|
||||||
linkMore,
|
linkMore,
|
||||||
overviews,
|
overviews,
|
||||||
extra,
|
extra,
|
||||||
|
metrics = [],
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [spinning, setSpinning] = useState(false);
|
const [spinning, setSpinning] = useState(false);
|
||||||
|
@ -35,12 +37,14 @@ export default (props) => {
|
||||||
max: "now",
|
max: "now",
|
||||||
timeFormatter: formatter.dates(1),
|
timeFormatter: formatter.dates(1),
|
||||||
},
|
},
|
||||||
|
timeInterval: '',
|
||||||
|
timeout: localStorage.getItem(TIMEOUT_CACHE_KEY) || '120s',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [refresh, setRefresh] = useState({ isRefreshPaused: false });
|
const [refresh, setRefresh] = useState({ isRefreshPaused: false });
|
||||||
const [timeZone, setTimeZone] = useState(() => getTimezone());
|
const [timeZone, setTimeZone] = useState(() => getTimezone());
|
||||||
|
|
||||||
const handleTimeChange = ({ start, end }) => {
|
const handleTimeChange = ({ start, end, timeInterval, timeout }) => {
|
||||||
const bounds = calculateBounds({
|
const bounds = calculateBounds({
|
||||||
from: start,
|
from: start,
|
||||||
to: end,
|
to: end,
|
||||||
|
@ -55,6 +59,8 @@ export default (props) => {
|
||||||
max: end,
|
max: end,
|
||||||
timeFormatter: formatter.dates(intDay),
|
timeFormatter: formatter.dates(intDay),
|
||||||
},
|
},
|
||||||
|
timeInterval: timeInterval || state.timeInterval,
|
||||||
|
timeout: timeout || state.timeout
|
||||||
});
|
});
|
||||||
setSpinning(true);
|
setSpinning(true);
|
||||||
};
|
};
|
||||||
|
@ -90,23 +96,35 @@ export default (props) => {
|
||||||
{...refresh}
|
{...refresh}
|
||||||
onRefreshChange={setRefresh}
|
onRefreshChange={setRefresh}
|
||||||
onRefresh={handleTimeChange}
|
onRefresh={handleTimeChange}
|
||||||
|
showTimeSetting={true}
|
||||||
|
showTimeInterval={true}
|
||||||
|
showTimeout={true}
|
||||||
|
timeout={state.timeout}
|
||||||
|
onTimeSettingChange={(timeSetting) => {
|
||||||
|
localStorage.setItem(TIMEOUT_CACHE_KEY, timeSetting.timeout)
|
||||||
|
setState({
|
||||||
|
...state,
|
||||||
|
timeInterval: timeSetting.timeInterval,
|
||||||
|
timeout: timeSetting.timeout
|
||||||
|
});
|
||||||
|
}}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
onTimeZoneChange={setTimeZone}
|
onTimeZoneChange={setTimeZone}
|
||||||
recentlyUsedRangesKey={'overview-detail'}
|
recentlyUsedRangesKey={'overview-detail'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={() => {
|
|
||||||
handleTimeChange({ start: state.timeRange.min, end: state.timeRange.max})
|
|
||||||
}} loading={spinning} icon={"reload"} type="primary" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.metricWrapper}>
|
<div className={styles.metricWrapper}>
|
||||||
<MetricSeries
|
<MetricSeries
|
||||||
action={metricAction}
|
action={metricAction}
|
||||||
timeRange={state.timeRange}
|
timeZone={timeZone}
|
||||||
overview={1}
|
overview={1}
|
||||||
setSpinning={setSpinning}
|
setSpinning={setSpinning}
|
||||||
renderExtraMetric={renderExtraMetric}
|
renderExtraMetric={renderExtraMetric}
|
||||||
|
metrics={metrics}
|
||||||
|
{...state}
|
||||||
|
bucketSize={state.timeInterval}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ const formatTimeout = (timeout) => {
|
||||||
return timeout
|
return timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
const TIMEOUT_CACHE_KEY = "monitor-timeout"
|
export const TIMEOUT_CACHE_KEY = "monitor-timeout"
|
||||||
|
|
||||||
const Monitor = (props) => {
|
const Monitor = (props) => {
|
||||||
const {
|
const {
|
||||||
|
@ -187,7 +187,6 @@ const Monitor = (props) => {
|
||||||
setSpinning={setSpinning}
|
setSpinning={setSpinning}
|
||||||
{...extraParams}
|
{...extraParams}
|
||||||
bucketSize={state.timeInterval}
|
bucketSize={state.timeInterval}
|
||||||
timeout={state.timeout}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -33,6 +33,7 @@ export default (props) => {
|
||||||
params={{ clusterID, clusterName }}
|
params={{ clusterID, clusterName }}
|
||||||
linkMore={`/cluster/monitor/elasticsearch/${clusterID}`}
|
linkMore={`/cluster/monitor/elasticsearch/${clusterID}`}
|
||||||
overviews={overviews}
|
overviews={overviews}
|
||||||
|
metrics={['index_throughput', 'search_throughput', 'index_latency', 'search_latency']}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import IndexMetric from "../../components/index_metric";
|
||||||
import ClusterMetric from "../../components/cluster_metric";
|
import ClusterMetric from "../../components/cluster_metric";
|
||||||
import QueueMetric from "../../components/queue_metric";
|
import QueueMetric from "../../components/queue_metric";
|
||||||
import { ESPrefix } from "@/services/common";
|
import { ESPrefix } from "@/services/common";
|
||||||
|
import { SearchEngines } from "@/lib/search_engines";
|
||||||
|
|
||||||
const timezone = "local";
|
const timezone = "local";
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ export default ({
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const isVersionGTE6 = useMemo(() => {
|
const isVersionGTE6 = useMemo(() => {
|
||||||
|
if ([SearchEngines.Easysearch, SearchEngines.Opensearch].includes(selectedCluster?.distribution)) return true;
|
||||||
const main = selectedCluster?.version?.split('.')[0]
|
const main = selectedCluster?.version?.split('.')[0]
|
||||||
if (main && parseInt(main) >= 6) {
|
if (main && parseInt(main) >= 6) {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -26,6 +26,12 @@ export default (props) => {
|
||||||
params={{ clusterID, clusterName }}
|
params={{ clusterID, clusterName }}
|
||||||
linkMore={`/cluster/monitor/${clusterID}/indices/${indexName}?_g={"cluster_name":"${clusterName}"}`}
|
linkMore={`/cluster/monitor/${clusterID}/indices/${indexName}?_g={"cluster_name":"${clusterName}"}`}
|
||||||
overviews={overviews}
|
overviews={overviews}
|
||||||
|
metrics={[
|
||||||
|
"index_throughput",
|
||||||
|
"search_throughput",
|
||||||
|
"index_latency",
|
||||||
|
"search_latency",
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,14 @@ export default (props) => {
|
||||||
params={{ clusterID, clusterName }}
|
params={{ clusterID, clusterName }}
|
||||||
linkMore={`/cluster/monitor/${clusterID}/nodes/${nodeID}?_g={"cluster_name":"${clusterName}","node_name":"${nodeName}"}`}
|
linkMore={`/cluster/monitor/${clusterID}/nodes/${nodeID}?_g={"cluster_name":"${clusterName}","node_name":"${nodeName}"}`}
|
||||||
overviews={overviews}
|
overviews={overviews}
|
||||||
|
metrics={[
|
||||||
|
"cpu",
|
||||||
|
"jvm",
|
||||||
|
"index_throughput",
|
||||||
|
"search_throughput",
|
||||||
|
"index_latency",
|
||||||
|
"search_latency",
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Tabs } from "antd";
|
||||||
import NodeMetric from "../../components/node_metric";
|
import NodeMetric from "../../components/node_metric";
|
||||||
import QueueMetric from "../../components/queue_metric";
|
import QueueMetric from "../../components/queue_metric";
|
||||||
import { formatMessage } from "umi/locale";
|
import { formatMessage } from "umi/locale";
|
||||||
|
import { SearchEngines } from "@/lib/search_engines";
|
||||||
|
|
||||||
const timezone = "local";
|
const timezone = "local";
|
||||||
|
|
||||||
|
@ -17,12 +18,13 @@ export default ({
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const isVersionGTE6 = useMemo(() => {
|
const isVersionGTE6 = useMemo(() => {
|
||||||
|
if ([SearchEngines.Easysearch, SearchEngines.Opensearch].includes(selectedCluster?.distribution)) return true;
|
||||||
const main = selectedCluster?.version?.split('.')[0]
|
const main = selectedCluster?.version?.split('.')[0]
|
||||||
if (main && parseInt(main) >= 6) {
|
if (main && parseInt(main) >= 6) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}, [selectedCluster?.version])
|
}, [selectedCluster])
|
||||||
|
|
||||||
const [param, setParam] = useState({
|
const [param, setParam] = useState({
|
||||||
show_top: false,
|
show_top: false,
|
||||||
|
|
|
@ -34,7 +34,9 @@ export default (props) => {
|
||||||
queryParams,
|
queryParams,
|
||||||
className,
|
className,
|
||||||
style,
|
style,
|
||||||
formatMetric
|
formatMetric,
|
||||||
|
height = 200,
|
||||||
|
customRenderChart
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
@ -71,7 +73,7 @@ export default (props) => {
|
||||||
} else if (res && !res.error) {
|
} else if (res && !res.error) {
|
||||||
const { metrics = {} } = res || {};
|
const { metrics = {} } = res || {};
|
||||||
const metric = metrics[metricKey]
|
const metric = metrics[metricKey]
|
||||||
setMetric(formatMetric ? formatMetric(metric) : metric);
|
setMetric(formatMetric && metric ? formatMetric(metric) : metric);
|
||||||
}
|
}
|
||||||
if (firstFetchRef.current || showLoading) {
|
if (firstFetchRef.current || showLoading) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
@ -142,10 +144,10 @@ export default (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderChart = () => {
|
const renderChart = () => {
|
||||||
if (loading) return <div style={{ height: 200 }}></div>
|
if (loading) return <div style={{ height }}></div>
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div style={{ height: 200, padding: 10 }}>
|
<div style={{ height, padding: 10 }}>
|
||||||
<Alert style={{ maxHeight: '100%', overflow: 'auto', wordBreak: 'break-all'}} message={error} type="error"/>
|
<Alert style={{ maxHeight: '100%', overflow: 'auto', wordBreak: 'break-all'}} message={error} type="error"/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -153,11 +155,11 @@ export default (props) => {
|
||||||
const axis = metric?.axis || [];
|
const axis = metric?.axis || [];
|
||||||
const lines = metric?.lines || [];
|
const lines = metric?.lines || [];
|
||||||
if (lines.every((item) => !item.data || item.data.length === 0)) {
|
if (lines.every((item) => !item.data || item.data.length === 0)) {
|
||||||
return <Empty style={{ height: 200, margin: 0, paddingTop: 64 }} image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
return <Empty style={{ height, margin: 0, paddingTop: 64 }} image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Chart
|
<Chart
|
||||||
size={[, 200]}
|
size={[, height]}
|
||||||
className={styles.vizChartItem}
|
className={styles.vizChartItem}
|
||||||
ref={chartRef}
|
ref={chartRef}
|
||||||
>
|
>
|
||||||
|
@ -312,7 +314,7 @@ export default (props) => {
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
{renderChart()}
|
{customRenderChart ? customRenderChart(metric) : renderChart()}
|
||||||
</Spin>
|
</Spin>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue