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) {

Reply via email to