mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-09-20 13:09:27 +00:00
fix: split queries for topology to avoid page crash (#485)
This commit is contained in:
@@ -113,4 +113,6 @@ export const LightChartColors = [
|
||||
"#c4ccd3",
|
||||
];
|
||||
|
||||
export const MaxQueryLength = 120;
|
||||
export const TopologyMaxQueryEntities = 20;
|
||||
export const TopologyMaxQueryExpressions = 10;
|
||||
export const DashboardMaxQueryWidgets = 6;
|
||||
|
@@ -14,7 +14,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { RespFields, MaximumEntities, MaxQueryLength } from "./data";
|
||||
import {
|
||||
RespFields,
|
||||
MaximumEntities,
|
||||
TopologyMaxQueryEntities,
|
||||
TopologyMaxQueryExpressions,
|
||||
DashboardMaxQueryWidgets,
|
||||
} from "./data";
|
||||
import { EntityType, ExpressionResultType } from "@/views/dashboard/data";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useTopologyStore } from "@/store/modules/topology";
|
||||
@@ -26,10 +32,17 @@ import type { Instance, Endpoint, Service } from "@/types/selector";
|
||||
import type { Node, Call } from "@/types/topology";
|
||||
|
||||
function chunkArray(array: any[], chunkSize: number) {
|
||||
if (chunkSize <= 0) {
|
||||
return [array];
|
||||
}
|
||||
if (chunkSize > array.length) {
|
||||
return [array];
|
||||
}
|
||||
const result = [];
|
||||
for (let i = 0; i < array.length; i += chunkSize) {
|
||||
result.push(array.slice(i, i + chunkSize));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -191,7 +204,7 @@ export async function useDashboardQueryProcessor(configList: Indexable[]) {
|
||||
}
|
||||
}
|
||||
|
||||
const partArr = chunkArray(configList, 6);
|
||||
const partArr = chunkArray(configList, DashboardMaxQueryWidgets);
|
||||
const promiseArr = partArr.map((d: Array<Indexable>) => fetchMetrics(d));
|
||||
const responseList = await Promise.all(promiseArr);
|
||||
let resp = {};
|
||||
@@ -201,7 +214,6 @@ export async function useDashboardQueryProcessor(configList: Indexable[]) {
|
||||
...item,
|
||||
};
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
@@ -395,13 +407,14 @@ export async function useExpressionsQueryPodsMetrics(
|
||||
export function useQueryTopologyExpressionsProcessor(metrics: string[], instances: (Call | Node)[]) {
|
||||
const appStore = useAppStoreWithOut();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const topologyStore = useTopologyStore();
|
||||
|
||||
function getExpressionQuery(partMetrics?: string[]) {
|
||||
function getExpressionQuery(partMetrics: string[] = [], entities: (Call | Node)[] = []) {
|
||||
const conditions: { [key: string]: unknown } = {
|
||||
duration: appStore.durationTime,
|
||||
};
|
||||
const variables: string[] = [`$duration: Duration!`];
|
||||
const fragmentList = instances.map((d: any, index: number) => {
|
||||
const fragmentList = entities.map((d: Indexable, index: number) => {
|
||||
let serviceName;
|
||||
let destServiceName;
|
||||
let endpointName;
|
||||
@@ -482,23 +495,27 @@ export function useQueryTopologyExpressionsProcessor(metrics: string[], instance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
async function fetchMetrics(partMetrics: string[]) {
|
||||
const topologyStore = useTopologyStore();
|
||||
const param = getExpressionQuery(partMetrics);
|
||||
async function fetchMetrics(partMetrics: string[], entities: (Call | Node)[]) {
|
||||
const param = getExpressionQuery(partMetrics, entities);
|
||||
const res = await topologyStore.getTopologyExpressionValue(param);
|
||||
if (res.errors) {
|
||||
ElMessage.error(res.errors);
|
||||
return;
|
||||
}
|
||||
|
||||
return handleExpressionValues(partMetrics, res.data);
|
||||
}
|
||||
|
||||
async function getMetrics() {
|
||||
const count = Math.floor(MaxQueryLength / instances.length);
|
||||
const metricsArr = chunkArray(metrics, count);
|
||||
const promiseArr = metricsArr.map((d: string[]) => fetchMetrics(d));
|
||||
const metricsArr = chunkArray(metrics, TopologyMaxQueryExpressions);
|
||||
const entities = chunkArray(instances, TopologyMaxQueryEntities);
|
||||
|
||||
const promiseArr = metricsArr
|
||||
.map((d: string[]) => entities.map((e: (Call | Node)[]) => fetchMetrics(d, e)))
|
||||
.flat(1);
|
||||
const responseList = await Promise.all(promiseArr);
|
||||
let resp = {};
|
||||
for (const item of responseList) {
|
||||
@@ -507,8 +524,9 @@ export function useQueryTopologyExpressionsProcessor(metrics: string[], instance
|
||||
...item,
|
||||
};
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
return { getMetrics, getExpressionQuery };
|
||||
return { getMetrics };
|
||||
}
|
||||
|
@@ -223,14 +223,14 @@ export const topologyStore = defineStore({
|
||||
setNodeMetricValue(m: MetricVal) {
|
||||
this.nodeMetricValue = m;
|
||||
},
|
||||
setLegendValues(expressions: string, data: { [key: string]: any }) {
|
||||
setLegendValues(expression: string, data: Indexable) {
|
||||
const nodeArr = this.nodes.filter((d: Node) => d.isReal);
|
||||
|
||||
for (let idx = 0; idx < nodeArr.length; idx++) {
|
||||
for (let index = 0; index < expressions.length; index++) {
|
||||
const k = "expression" + idx + index;
|
||||
if (expressions[index]) {
|
||||
nodeArr[idx][expressions[index]] = Number(data[k].results[0].values[0].value);
|
||||
}
|
||||
if (expression) {
|
||||
nodeArr[idx][expression] = Number(
|
||||
data[expression]?.values?.find((d: { id: string; value: string }) => d.id === nodeArr[idx].id)?.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -158,7 +158,6 @@ limitations under the License. -->
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useTopologyStore } from "@/store/modules/topology";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { ScopeType, EntityType, CallTypes } from "@/views/dashboard/data";
|
||||
import type { Option } from "@/types/app";
|
||||
import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor";
|
||||
@@ -239,17 +238,12 @@ limitations under the License. -->
|
||||
async function setLegend() {
|
||||
updateSettings();
|
||||
const expression = dashboardStore.selectedGrid.legendMQE && dashboardStore.selectedGrid.legendMQE.expression;
|
||||
const { getExpressionQuery } = useQueryTopologyExpressionsProcessor(
|
||||
const { getMetrics } = useQueryTopologyExpressionsProcessor(
|
||||
[expression],
|
||||
topologyStore.nodes.filter((d: Node) => d.isReal),
|
||||
);
|
||||
const param = getExpressionQuery();
|
||||
const res = await topologyStore.getTopologyExpressionValue(param);
|
||||
if (res.errors) {
|
||||
ElMessage.error(res.errors);
|
||||
} else {
|
||||
topologyStore.setLegendValues([expression], res.data);
|
||||
}
|
||||
const metrics = await getMetrics();
|
||||
topologyStore.setLegendValues(expression, metrics);
|
||||
}
|
||||
function changeNodeDashboard(opt: any) {
|
||||
states.nodeDashboard = opt[0].value;
|
||||
|
@@ -90,7 +90,7 @@ limitations under the License. -->
|
||||
|
||||
const htmlServer = serverMetrics.map((m, index) => {
|
||||
const metric =
|
||||
topologyStore.linkServerMetrics[m].values.find((val: { id: string; value: unknown }) => val.id === data.id) ||
|
||||
topologyStore.linkServerMetrics[m]?.values?.find((val: { id: string; value: unknown }) => val.id === data.id) ||
|
||||
{};
|
||||
if (metric) {
|
||||
const opt: MetricConfigOpt = linkServerMetricConfig[index] || {};
|
||||
@@ -103,7 +103,7 @@ limitations under the License. -->
|
||||
const htmlClient = clientMetrics.map((m, index) => {
|
||||
const opt: MetricConfigOpt = linkClientMetricConfig[index] || {};
|
||||
const metric =
|
||||
topologyStore.linkClientMetrics[m].values.find((val: { id: string; value: unknown }) => val.id === data.id) ||
|
||||
topologyStore.linkClientMetrics[m]?.values?.find((val: { id: string; value: unknown }) => val.id === data.id) ||
|
||||
{};
|
||||
|
||||
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${opt.unit || ""}</div>`;
|
||||
@@ -122,7 +122,8 @@ limitations under the License. -->
|
||||
const nodeMetricConfig = props.settings.nodeMetricConfig || [];
|
||||
const html = nodeMetrics.map((m, index) => {
|
||||
const metric =
|
||||
topologyStore.nodeMetricValue[m].values.find((val: { id: string; value: unknown }) => val.id === data.id) || {};
|
||||
topologyStore.nodeMetricValue[m]?.values?.find((val: { id: string; value: unknown }) => val.id === data.id) ||
|
||||
{};
|
||||
const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
|
||||
|
||||
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value} ${opt.unit || ""}</div>`;
|
||||
|
@@ -159,7 +159,7 @@ limitations under the License. -->
|
||||
import zoom from "@/views/dashboard/related/components/utils/zoom";
|
||||
import { useQueryTopologyExpressionsProcessor } from "@/hooks/useExpressionsProcessor";
|
||||
import { ConfigFieldTypes } from "@/views/dashboard/data";
|
||||
/*global Nullable, defineProps */
|
||||
/*global Nullable, defineProps, Indexable */
|
||||
const props = defineProps({
|
||||
config: {
|
||||
type: Object as PropType<any>,
|
||||
@@ -285,20 +285,16 @@ limitations under the License. -->
|
||||
if (!expression) {
|
||||
return;
|
||||
}
|
||||
const { getExpressionQuery } = useQueryTopologyExpressionsProcessor(
|
||||
const { getMetrics } = useQueryTopologyExpressionsProcessor(
|
||||
[expression],
|
||||
topologyStore.nodes.filter((d: Node) => d.isReal),
|
||||
);
|
||||
const param = getExpressionQuery();
|
||||
const res = await topologyStore.getTopologyExpressionValue(param);
|
||||
if (res.errors) {
|
||||
ElMessage.error(res.errors);
|
||||
} else {
|
||||
topologyStore.setLegendValues([expression], res.data);
|
||||
}
|
||||
const metrics = await getMetrics();
|
||||
|
||||
topologyStore.setLegendValues(expression, metrics);
|
||||
}
|
||||
|
||||
function getNodeStatus(d: any) {
|
||||
function getNodeStatus(d: Indexable) {
|
||||
const { legendMQE } = settings.value;
|
||||
if (!legendMQE) {
|
||||
return icons.CUBE;
|
||||
@@ -313,8 +309,7 @@ limitations under the License. -->
|
||||
const nodeMetricConfig = settings.value.nodeMetricConfig || [];
|
||||
const html = nodeMetrics.map((m, index) => {
|
||||
const metric =
|
||||
(topologyStore.nodeMetricValue[m] &&
|
||||
topologyStore.nodeMetricValue[m].values.find((val: { id: string; value: string }) => val.id === data.id)) ||
|
||||
topologyStore.nodeMetricValue[m]?.values?.find((val: { id: string; value: string }) => val.id === data.id) ||
|
||||
{};
|
||||
const opt: MetricConfigOpt = nodeMetricConfig[index] || {};
|
||||
return ` <div class="mb-5"><span class="grey">${opt.label || m}: </span>${metric.value || NaN} ${
|
||||
@@ -339,7 +334,7 @@ limitations under the License. -->
|
||||
const linkClientMetricConfig: MetricConfigOpt[] = settings.value.linkClientMetricConfig || [];
|
||||
const linkServerMetrics: string[] = settings.value.linkServerExpressions || [];
|
||||
const htmlServer = linkServerMetrics.map((m, index) => {
|
||||
const metric = topologyStore.linkServerMetrics[m].values.find(
|
||||
const metric = topologyStore.linkServerMetrics[m]?.values?.find(
|
||||
(val: { id: string; value: unknown }) => val.id === data.id,
|
||||
);
|
||||
if (metric) {
|
||||
@@ -351,7 +346,7 @@ limitations under the License. -->
|
||||
});
|
||||
const htmlClient = linkClientMetrics.map((m: string, index: number) => {
|
||||
const opt: MetricConfigOpt = linkClientMetricConfig[index] || {};
|
||||
const metric = topologyStore.linkClientMetrics[m].values.find(
|
||||
const metric = topologyStore.linkClientMetrics[m]?.values?.find(
|
||||
(val: { id: string; value: unknown }) => val.id === data.id,
|
||||
);
|
||||
if (metric) {
|
||||
|
Reference in New Issue
Block a user