This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/skywalking-booster-ui.git
The following commit(s) were added to refs/heads/main by this push: new ad4b0639 fix: split queries for topology to avoid page crash (#485) ad4b0639 is described below commit ad4b0639cd9bef691a12f894d95d385fb44e889c Author: Fine0830 <fanxue0...@gmail.com> AuthorDate: Wed Jul 30 20:52:33 2025 +0800 fix: split queries for topology to avoid page crash (#485) --- src/hooks/data.ts | 4 ++- src/hooks/useExpressionsProcessor.ts | 42 +++++++++++++++------- src/store/modules/topology.ts | 12 +++---- .../dashboard/related/topology/config/Settings.vue | 12 ++----- .../dashboard/related/topology/pod/Sankey.vue | 7 ++-- .../related/topology/service/ServiceMap.vue | 23 +++++------- 6 files changed, 55 insertions(+), 45 deletions(-) diff --git a/src/hooks/data.ts b/src/hooks/data.ts index f797fba6..db49cf32 100644 --- a/src/hooks/data.ts +++ b/src/hooks/data.ts @@ -113,4 +113,6 @@ export const LightChartColors = [ "#c4ccd3", ]; -export const MaxQueryLength = 120; +export const TopologyMaxQueryEntities = 20; +export const TopologyMaxQueryExpressions = 10; +export const DashboardMaxQueryWidgets = 6; diff --git a/src/hooks/useExpressionsProcessor.ts b/src/hooks/useExpressionsProcessor.ts index b4b54598..4bdeb728 100644 --- a/src/hooks/useExpressionsProcessor.ts +++ b/src/hooks/useExpressionsProcessor.ts @@ -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 }; } diff --git a/src/store/modules/topology.ts b/src/store/modules/topology.ts index 081aa4be..6dc332eb 100644 --- a/src/store/modules/topology.ts +++ b/src/store/modules/topology.ts @@ -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, + ); } } }, diff --git a/src/views/dashboard/related/topology/config/Settings.vue b/src/views/dashboard/related/topology/config/Settings.vue index 4e8602e3..14ea596b 100644 --- a/src/views/dashboard/related/topology/config/Settings.vue +++ b/src/views/dashboard/related/topology/config/Settings.vue @@ -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; diff --git a/src/views/dashboard/related/topology/pod/Sankey.vue b/src/views/dashboard/related/topology/pod/Sankey.vue index 5b45e0b6..feb054a0 100644 --- a/src/views/dashboard/related/topology/pod/Sankey.vue +++ b/src/views/dashboard/related/topology/pod/Sankey.vue @@ -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>`; diff --git a/src/views/dashboard/related/topology/service/ServiceMap.vue b/src/views/dashboard/related/topology/service/ServiceMap.vue index cf3b1707..afa7f9a3 100644 --- a/src/views/dashboard/related/topology/service/ServiceMap.vue +++ b/src/views/dashboard/related/topology/service/ServiceMap.vue @@ -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) {