This is an automated email from the ASF dual-hosted git repository.

qiuxiafan 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 536df8c0 feat: Split topology metric query to avoid exceeding the 
maximum query complexity (#429)
536df8c0 is described below

commit 536df8c052c68820947de55f82dc2e873a69e828
Author: Fine0830 <[email protected]>
AuthorDate: Thu Nov 14 11:38:13 2024 +0800

    feat: Split topology metric query to avoid exceeding the maximum query 
complexity (#429)
---
 src/hooks/data.ts                                  |  2 +
 src/hooks/useExpressionsProcessor.ts               | 63 +++++++++++++++-------
 src/store/modules/topology.ts                      | 40 ++++----------
 .../dashboard/related/topology/config/Settings.vue |  2 +-
 .../related/topology/service/ServiceMap.vue        |  2 +-
 5 files changed, 58 insertions(+), 51 deletions(-)

diff --git a/src/hooks/data.ts b/src/hooks/data.ts
index 1ad8867e..f797fba6 100644
--- a/src/hooks/data.ts
+++ b/src/hooks/data.ts
@@ -112,3 +112,5 @@ export const LightChartColors = [
   "#546570",
   "#c4ccd3",
 ];
+
+export const MaxQueryLength = 120;
diff --git a/src/hooks/useExpressionsProcessor.ts 
b/src/hooks/useExpressionsProcessor.ts
index c086b520..ecd74cfc 100644
--- a/src/hooks/useExpressionsProcessor.ts
+++ b/src/hooks/useExpressionsProcessor.ts
@@ -14,9 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { RespFields, MaximumEntities } from "./data";
+import { RespFields, MaximumEntities, MaxQueryLength } from "./data";
 import { EntityType, ExpressionResultType } from "@/views/dashboard/data";
 import { ElMessage } from "element-plus";
+import { useTopologyStore } from "@/store/modules/topology";
 import { useDashboardStore } from "@/store/modules/dashboard";
 import { useSelectorStore } from "@/store/modules/selectors";
 import { useAppStoreWithOut } from "@/store/modules/app";
@@ -24,6 +25,14 @@ import type { MetricConfigOpt } from "@/types/dashboard";
 import type { Instance, Endpoint, Service } from "@/types/selector";
 import type { Node, Call } from "@/types/topology";
 
+function chunkArray(array: any[], chunkSize: number) {
+  const result = [];
+  for (let i = 0; i < array.length; i += chunkSize) {
+    result.push(array.slice(i, i + chunkSize));
+  }
+  return result;
+}
+
 export async function useDashboardQueryProcessor(configList: Indexable[]) {
   function expressionsGraphql(config: Indexable, idx: number) {
     if (!(config.metrics && config.metrics[0])) {
@@ -181,13 +190,6 @@ export async function 
useDashboardQueryProcessor(configList: Indexable[]) {
       return { 0: { source: {}, tips: [], typesOfMQE: [] } };
     }
   }
-  function chunkArray(array: any[], chunkSize: number) {
-    const result = [];
-    for (let i = 0; i < array.length; i += chunkSize) {
-      result.push(array.slice(i, i + chunkSize));
-    }
-    return result;
-  }
 
   const partArr = chunkArray(configList, 6);
   const promiseArr = partArr.map((d: Array<Indexable>) => fetchMetrics(d));
@@ -394,7 +396,7 @@ export function 
useQueryTopologyExpressionsProcessor(metrics: string[], instance
   const appStore = useAppStoreWithOut();
   const dashboardStore = useDashboardStore();
 
-  function getExpressionQuery() {
+  function getExpressionQuery(partMetrics?: string[]) {
     const conditions: { [key: string]: unknown } = {
       duration: appStore.durationTime,
     };
@@ -448,7 +450,7 @@ export function 
useQueryTopologyExpressionsProcessor(metrics: string[], instance
       };
       variables.push(`$entity${index}: Entity!`);
       conditions[`entity${index}`] = entity;
-      const f = metrics.map((name: string, idx: number) => {
+      const f = (partMetrics || metrics).map((name: string, idx: number) => {
         if (index === 0) {
           variables.push(`$expression${idx}: String!`);
           conditions[`expression${idx}`] = name;
@@ -462,19 +464,19 @@ export function 
useQueryTopologyExpressionsProcessor(metrics: string[], instance
 
     return { queryStr, conditions };
   }
-  function handleExpressionValues(resp: { [key: string]: any }) {
+  function handleExpressionValues(partMetrics: string[], resp: { [key: 
string]: any }) {
     const obj: any = {};
     for (let idx = 0; idx < instances.length; idx++) {
-      for (let index = 0; index < metrics.length; index++) {
+      for (let index = 0; index < partMetrics.length; index++) {
         const k = "expression" + idx + index;
-        if (metrics[index]) {
-          if (!obj[metrics[index]]) {
-            obj[metrics[index]] = {
+        if (partMetrics[index]) {
+          if (!obj[partMetrics[index]]) {
+            obj[partMetrics[index]] = {
               values: [],
             };
           }
-          obj[metrics[index]].values.push({
-            value: resp[k].results[0] && resp[k].results[0].values[0].value,
+          obj[partMetrics[index]].values.push({
+            value: resp[k] && resp[k].results[0] && 
resp[k].results[0].values[0].value,
             id: instances[idx].id,
           });
         }
@@ -482,6 +484,31 @@ export function 
useQueryTopologyExpressionsProcessor(metrics: string[], instance
     }
     return obj;
   }
+  async function fetchMetrics(partMetrics: string[]) {
+    const topologyStore = useTopologyStore();
+    const param = getExpressionQuery(partMetrics);
+    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 responseList = await Promise.all(promiseArr);
+    let resp = {};
+    for (const item of responseList) {
+      resp = {
+        ...resp,
+        ...item,
+      };
+    }
+    return resp;
+  }
 
-  return { getExpressionQuery, handleExpressionValues };
+  return { getMetrics, getExpressionQuery };
 }
diff --git a/src/store/modules/topology.ts b/src/store/modules/topology.ts
index 66354f01..c5379cfd 100644
--- a/src/store/modules/topology.ts
+++ b/src/store/modules/topology.ts
@@ -24,7 +24,6 @@ import { useAppStoreWithOut } from "@/store/modules/app";
 import type { AxiosResponse } from "axios";
 import query from "@/graphql/fetch";
 import { useQueryTopologyExpressionsProcessor } from 
"@/hooks/useExpressionsProcessor";
-import { ElMessage } from "element-plus";
 
 interface MetricVal {
   [key: string]: { values: { id: string; value: unknown }[] };
@@ -443,7 +442,7 @@ export const topologyStore = defineStore({
 
       return { calls, nodes };
     },
-    async getNodeExpressionValue(param: { queryStr: string; conditions: { 
[key: string]: unknown } }) {
+    async getTopologyExpressionValue(param: { queryStr: string; conditions: { 
[key: string]: unknown } }) {
       const res: AxiosResponse = await query(param);
 
       if (res.data.errors) {
@@ -461,14 +460,8 @@ export const topologyStore = defineStore({
       if (!calls.length) {
         return;
       }
-      const { getExpressionQuery, handleExpressionValues } = 
useQueryTopologyExpressionsProcessor(expressions, calls);
-      const param = getExpressionQuery();
-      const res = await this.getNodeExpressionValue(param);
-      if (res.errors) {
-        ElMessage.error(res.errors);
-        return;
-      }
-      const metrics = handleExpressionValues(res.data);
+      const { getMetrics } = useQueryTopologyExpressionsProcessor(expressions, 
calls);
+      const metrics = await getMetrics();
       if (type === "SERVER") {
         this.setLinkServerMetrics(metrics);
       } else {
@@ -484,17 +477,11 @@ export const topologyStore = defineStore({
         this.setNodeMetricValue({});
         return;
       }
-      const { getExpressionQuery, handleExpressionValues } = 
useQueryTopologyExpressionsProcessor(
+      const { getMetrics } = useQueryTopologyExpressionsProcessor(
         expressions,
         this.nodes.filter((d: Node) => d.isReal),
       );
-      const param = getExpressionQuery();
-      const res = await this.getNodeExpressionValue(param);
-      if (res.errors) {
-        ElMessage.error(res.errors);
-        return;
-      }
-      const metrics = handleExpressionValues(res.data);
+      const metrics = await getMetrics();
       this.setNodeMetricValue(metrics);
     },
     async getHierarchyServiceTopology() {
@@ -550,17 +537,6 @@ export const topologyStore = defineStore({
       
this.setHierarchyInstanceTopology(res.data.data.hierarchyInstanceTopology || 
{}, levels);
       return res.data;
     },
-    async queryHierarchyExpressions(expressions: string[], nodes: Node[]) {
-      const { getExpressionQuery, handleExpressionValues } = 
useQueryTopologyExpressionsProcessor(expressions, nodes);
-      const param = getExpressionQuery();
-      const res = await this.getNodeExpressionValue(param);
-      if (res.errors) {
-        ElMessage.error(res.errors);
-        return;
-      }
-      const metrics = handleExpressionValues(res.data);
-      return metrics;
-    },
     async queryHierarchyNodeExpressions(expressions: string[], layer: string) {
       const nodes = this.hierarchyServiceNodes.filter((n: HierarchyNode) => 
n.layer === layer);
       if (!nodes.length) {
@@ -571,7 +547,8 @@ export const topologyStore = defineStore({
         this.setHierarchyNodeMetricValue({}, layer);
         return;
       }
-      const metrics = await this.queryHierarchyExpressions(expressions, nodes);
+      const { getMetrics } = useQueryTopologyExpressionsProcessor(expressions, 
nodes);
+      const metrics = await getMetrics();
       this.setHierarchyNodeMetricValue(metrics, layer);
     },
     async queryHierarchyInstanceNodeExpressions(expressions: string[], layer: 
string) {
@@ -585,7 +562,8 @@ export const topologyStore = defineStore({
         this.setHierarchyInstanceNodeMetricValue({}, layer);
         return;
       }
-      const metrics = await this.queryHierarchyExpressions(expressions, nodes);
+      const { getMetrics } = useQueryTopologyExpressionsProcessor(expressions, 
nodes);
+      const metrics = await getMetrics();
       this.setHierarchyInstanceNodeMetricValue(metrics, layer);
     },
   },
diff --git a/src/views/dashboard/related/topology/config/Settings.vue 
b/src/views/dashboard/related/topology/config/Settings.vue
index 6c0e0841..d8929b5b 100644
--- a/src/views/dashboard/related/topology/config/Settings.vue
+++ b/src/views/dashboard/related/topology/config/Settings.vue
@@ -249,7 +249,7 @@ limitations under the License. -->
       topologyStore.nodes.filter((d: Node) => d.isReal),
     );
     const param = getExpressionQuery();
-    const res = await topologyStore.getNodeExpressionValue(param);
+    const res = await topologyStore.getTopologyExpressionValue(param);
     if (res.errors) {
       ElMessage.error(res.errors);
     } else {
diff --git a/src/views/dashboard/related/topology/service/ServiceMap.vue 
b/src/views/dashboard/related/topology/service/ServiceMap.vue
index eba245d6..947b927c 100644
--- a/src/views/dashboard/related/topology/service/ServiceMap.vue
+++ b/src/views/dashboard/related/topology/service/ServiceMap.vue
@@ -290,7 +290,7 @@ limitations under the License. -->
       topologyStore.nodes.filter((d: Node) => d.isReal),
     );
     const param = getExpressionQuery();
-    const res = await topologyStore.getNodeExpressionValue(param);
+    const res = await topologyStore.getTopologyExpressionValue(param);
     if (res.errors) {
       ElMessage.error(res.errors);
     } else {

Reply via email to