Ver código fonte

review

Signed-off-by: min.tian <min.tian.cn@gmail.com>
min.tian 2 anos atrás
pai
commit
939c3af1ce

+ 3 - 2
client/src/context/Prometheus.tsx

@@ -82,8 +82,9 @@ export const PrometheusProvider = (props: { children: React.ReactNode }) => {
               LAST_TIME_PROMETHEUS_NAMESPACE,
               prometheusNamespace
             );
-            // openSnackBar(prometheusTrans('ready'));
-          } else openSnackBar(prometheusTrans('invalid'), 'error');
+          } else {
+            openSnackBar(prometheusTrans('invalid'), 'error');
+          }
           setIsPrometheusReady(isReady);
         })
         .catch(err => {

+ 4 - 0
client/src/i18n/cn/prometheus.ts

@@ -1,6 +1,10 @@
 const prometheusTrans = {
   ready: 'Prometheus is ready.',
   invalid: 'Prometheus configuration is invalid.',
+  
+  totalCount: 'Total Count',
+  searchCount: 'Search Count',
+  searchLatency: 'Search Latency',
 };
 
 export default prometheusTrans;

+ 4 - 0
client/src/i18n/en/prometheus.ts

@@ -1,6 +1,10 @@
 const prometheusTrans = {
   ready: 'Prometheus is ready.',
   invalid: 'Prometheus configuration is invalid.',
+
+  totalCount: 'Total Count',
+  searchCount: 'Search Count',
+  searchLatency: 'Search Latency',
 };
 
 export default prometheusTrans;

+ 10 - 10
client/src/pages/connect/AuthForm.tsx

@@ -159,26 +159,26 @@ export const AuthForm = (props: any) => {
         defaultValue: prometheusAddress,
       },
       {
-        label: `${attuTrans.prometheusInstance}`,
-        key: 'prometheus_instance',
-        onChange: setPrometheusInstance,
+        label: `${attuTrans.prometheusNamespace}`,
+        key: 'prometheus_namespace',
+        onChange: setPrometheusNamespace,
         variant: 'filled',
         className: classes.input,
-        placeholder: attuTrans.prometheusInstance,
+        placeholder: attuTrans.prometheusNamespace,
         fullWidth: true,
 
-        defaultValue: prometheusInstance,
+        defaultValue: prometheusNamespace,
       },
       {
-        label: `${attuTrans.prometheusNamespace}`,
-        key: 'prometheus_namespace',
-        onChange: setPrometheusNamespace,
+        label: `${attuTrans.prometheusInstance}`,
+        key: 'prometheus_instance',
+        onChange: setPrometheusInstance,
         variant: 'filled',
         className: classes.input,
-        placeholder: attuTrans.prometheusNamespace,
+        placeholder: attuTrans.prometheusInstance,
         fullWidth: true,
 
-        defaultValue: prometheusNamespace,
+        defaultValue: prometheusInstance,
       },
     ],
     []

+ 2 - 2
client/src/pages/systemHealthy/HealthyIndexDetailView.tsx

@@ -10,11 +10,11 @@ import { Dispatch, SetStateAction, useState } from 'react';
 const getStyles = makeStyles((theme: Theme) => ({
   mainView: {
     width: '100%',
-    marginTop: '16px',
+    marginTop: '20px',
   },
   healthyIndexItem: {
     display: 'flex',
-    marginTop: '6px',
+    marginTop: '8px',
     justifyContent: 'space-between',
   },
   healthyIndexLabel: {

+ 1 - 2
client/src/pages/systemHealthy/HealthyIndexOverview.tsx

@@ -28,8 +28,7 @@ const getStyles = makeStyles((theme: Theme) => ({
     width: `${MAIN_VIEW_WIDTH}px`,
     height: `${TOPO_HEIGHT}px`,
     overflow: 'auto',
-    padding: '12px 56px 0px 24px',
-    // boxShadow: '0 0 5px #ccc',
+    padding: '8px 56px 0px 24px',
     fontSize: '14px',
   },
   headerContent: {

+ 9 - 46
client/src/pages/systemHealthy/SystemHealthyView.tsx

@@ -5,23 +5,16 @@ import { useInterval } from '../../hooks/SystemView';
 import { PrometheusHttp } from '../../http/Prometheus';
 import { ALL_ROUTER_TYPES } from '../../router/Types';
 import {
-  EHealthyStatus,
   ENodeService,
-  ENodeType,
   ILineChartData,
   INodeTreeStructure,
   IPrometheusAllData,
-  IPrometheusNode,
   IThreshold,
   ITimeRangeOption,
 } from './Types';
-import clsx from 'clsx';
 import Topology from './Topology';
-import * as d3 from 'd3';
 import { reconNodeTree } from './dataHandler';
 import HealthyIndexOverview from './HealthyIndexOverview';
-import HealthyIndexDetailView from './HealthyIndexDetailView';
-import { KeyboardArrowDown } from '@material-ui/icons';
 import { timeRangeOptions } from './consts';
 import {
   LAST_TIME_HEALTHY_THRESHOLD_CPU,
@@ -31,57 +24,24 @@ import {
   DEFAULT_HEALTHY_THRESHOLD_CPU,
   DEFAULT_HEALTHY_THRESHOLD_MEMORY,
 } from '../../consts/Prometheus';
-// import data from "./data.json";
+import { useTranslation } from 'react-i18next';
 
 const getStyles = makeStyles((theme: Theme) => ({
   root: {
     fontFamily: 'Roboto',
-    margin: '14px 40px',
+    margin: '8px 40px',
     position: 'relative',
     height: 'fit-content',
     display: 'flex',
     flexDirection: 'column',
-    // border: '1px solid red',
   },
   mainView: {
     borderRadius: '8px',
     boxShadow: '3px 3px 10px rgba(0, 0, 0, 0.05)',
     display: 'grid',
     gridTemplateColumns: '1fr auto',
-    marginTop: '14px',
+    marginTop: '8px',
     height: '100%',
-    // border: '1px solid green',
-  },
-  detailView: {
-    height: '100%',
-    width: '100%',
-    transition: 'all .25s',
-    position: 'absolute',
-    // border: '1px solid purple',
-  },
-  showDetailView: {
-    top: 0,
-    minHeight: '100vh',
-    height: 'fit-content',
-  },
-  hideDetailView: {
-    top: '2000px',
-    maxHeight: 0,
-  },
-  detailViewContainer: {
-    borderRadius: '8px',
-    boxShadow: '3px 3px 10px rgba(0, 0, 0, 0.05)',
-    display: 'grid',
-    gridTemplateColumns: '1fr auto',
-    // marginTop: '14px',
-    height: '100%',
-  },
-  childCloseBtn: {
-    border: 0,
-    backgroundColor: 'white',
-    gridArea: 'a',
-    cursor: 'pointer',
-    width: '100%',
   },
 }));
 
@@ -126,20 +86,22 @@ const SystemHealthyView = () => {
           } as INodeTreeStructure),
     [prometheusData, threshold]
   );
+
+  const { t: prometheusTrans } = useTranslation('prometheus');
   const lineChartsData = useMemo<ILineChartData[]>(
     () =>
       prometheusData
         ? [
             {
-              label: 'Total Count',
+              label: prometheusTrans('totalCount'),
               data: prometheusData.totalVectorsCount,
             },
             {
-              label: 'Search Count',
+              label: prometheusTrans('searchCount'),
               data: prometheusData.searchVectorsCount,
             },
             {
-              label: 'Search Latency',
+              label: prometheusTrans('searchLatency'),
               data: prometheusData.sqLatency,
               format: d => d.toFixed(0),
               unit: 'ms',
@@ -194,6 +156,7 @@ const SystemHealthyView = () => {
             selectedService={selectedService}
             onClick={setSelectedService}
           />
+
           <HealthyIndexOverview
             selectedNode={selectedNode}
             lineChartsData={lineChartsData}

+ 58 - 4
client/src/pages/systemHealthy/Topology.tsx

@@ -1,5 +1,5 @@
 import { makeStyles, Theme, useTheme } from '@material-ui/core';
-import { Dispatch, memo } from 'react';
+import { Dispatch, memo, useContext } from 'react';
 import {
   TOPO_HEIGHT,
   TOPO_LINK_LENGTH,
@@ -9,6 +9,8 @@ import {
 import { getIcon } from './getIcon';
 import { ENodeService, ENodeType, INodeTreeStructure } from './Types';
 import clsx from 'clsx';
+import { formatPrometheusAddress } from '../../utils/Format';
+import { prometheusContext } from '../../context/Prometheus';
 
 const getStyles = makeStyles((theme: Theme) => ({
   root: {
@@ -16,8 +18,26 @@ const getStyles = makeStyles((theme: Theme) => ({
     borderBottomLeftRadius: '8px',
     overflow: 'auto',
     backgroundColor: 'white',
-    // overflow: 'visible',
-    minHeight: '90%',
+  },
+  prometheusInfoContainer: {
+    position: 'absolute',
+    display: 'flex',
+    fontSize: '12px',
+    padding: '4px 8px',
+    flexWrap: 'wrap',
+  },
+  prometheusInfoItem: {
+    marginRight: '20px',
+    display: 'flex',
+  },
+  prometheusInfoItemLabel: {
+    marginRight: '8px',
+    fontWeight: 600,
+    color: '#333',
+  },
+  prometheusInfoItemText: {
+    fontWeight: 500,
+    color: '#666',
   },
   node: {
     transition: 'all .25s',
@@ -55,7 +75,9 @@ const getStyles = makeStyles((theme: Theme) => ({
   },
 }));
 
-const randomList = Array(10).fill(0).map(_ => Math.random());
+const randomList = Array(10)
+  .fill(0)
+  .map(_ => Math.random());
 
 const nodesLayout = (
   nodes: INodeTreeStructure[],
@@ -102,8 +124,40 @@ const Topology = ({
   const { rootNode, childrenNodes, rootPos, childrenPos, subChildrenPos } =
     nodesLayout(nodeTree.children, width, height);
 
+  const { prometheusAddress, prometheusInstance, prometheusNamespace } =
+    useContext(prometheusContext);
+  const prometheusInfos = [
+    {
+      label: 'Prometheus Address',
+      value: formatPrometheusAddress(prometheusAddress),
+    },
+    {
+      label: 'Namespace',
+      value: prometheusNamespace,
+    },
+    {
+      label: 'Instance',
+      value: prometheusInstance,
+    },
+  ];
+
   return (
     <div className={classes.root}>
+      <div className={classes.prometheusInfoContainer}>
+        {prometheusInfos.map(prometheusInfo => (
+          <div
+            key={prometheusInfo.value}
+            className={classes.prometheusInfoItem}
+          >
+            <div className={classes.prometheusInfoItemLabel}>
+              {prometheusInfo.label}:
+            </div>
+            <div className={classes.prometheusInfoItemText}>
+              {prometheusInfo.value}
+            </div>
+          </div>
+        ))}
+      </div>
       <svg width={width} height={height} style={{ overflow: 'visible' }}>
         {childrenNodes.map((node, i) => {
           const childPos = childrenPos[i];

+ 3 - 3
client/src/pages/systemHealthy/consts.ts

@@ -1,7 +1,7 @@
 import { EHealthyStatus, ITimeRangeOption } from './Types';
 
 export const TOPO_WIDTH = 600;
-export const TOPO_HEIGHT = 560;
+export const TOPO_HEIGHT = 580;
 export const TOPO_NODE_R = [68, 45, 30];
 export const TOPO_LINK_LENGTH = [160, 270];
 
@@ -16,8 +16,8 @@ export const HEALTHY_STATUS_COLORS = {
   [EHealthyStatus.failed]: '#F16415',
 };
 
-export const LINE_CHART_LARGE_HEIGHT = 56;
-export const LINE_CHART_SMALL_HEIGHT = 42;
+export const LINE_CHART_LARGE_HEIGHT = 60;
+export const LINE_CHART_SMALL_HEIGHT = 48;
 export const LINE_COLOR = '#394E97';
 export const LINE_LABEL_Y_PADDING = 6;
 export const LINE_LABEL_FONT_SIZE = 14;

+ 34 - 0
server/src/prometheus/fillRangeData.ts

@@ -0,0 +1,34 @@
+export enum EPrometheusDataStatus {
+  noData = -1,
+  failed = -2,
+}
+
+export const fillRangeData = (
+  items: any[],
+  start: number,
+  end: number,
+  step: number
+) => {
+  const length = Math.floor((+end - start) / step) + 1;
+  if (length >= 0) {
+    const timeRange = Array(length)
+      .fill(0)
+      .map((_, i) => +start + i * step)
+      .map(d => d / 1000);
+
+    items.forEach(item => {
+      const dict = {} as any;
+      item.values.forEach(([t, v]: any) => (dict[t] = isNaN(v) ? 0 : +v));
+      const minTime = Math.min(...item.values.map((d: any) => d[0]));
+      item.values = timeRange.map(t =>
+        t in dict
+          ? dict[t]
+          : t > minTime
+          ? EPrometheusDataStatus.failed
+          : EPrometheusDataStatus.noData
+      );
+    });
+  }
+};
+
+export default fillRangeData;

+ 58 - 147
server/src/prometheus/prometheus.service.ts

@@ -1,4 +1,5 @@
 import axios from 'axios';
+import fillRangeData from './fillRangeData';
 
 interface IPrometheusNode {
   type: string;
@@ -118,58 +119,26 @@ export class PrometheusService {
       `&start=${new Date(+start).toISOString()}` +
       `&end=${new Date(+end).toISOString()}` +
       `&step=${step / 1000}s`;
-    return await http.get(url).then(res => res.data);
-  }
-
-  async getVectorsCount(
-    metric: string,
-    start: number,
-    end: number,
-    step: number
-  ) {
-    const expr = `${metric}${PrometheusService.selector}`;
-    const result = await this.queryRange(expr, start, end, step);
+    const result = await http.get(url).then(res => res.data);
     const data = result.data.result;
-    const length = Math.floor((end - start) / step);
-
-    if (data.length === 0) return Array(length).fill(0);
-
-    const res = result.data.result[0].values.map((d: any) => +d[1]).slice(1);
+    fillRangeData(data, start, end, step);
+    return data;
+  }
 
-    let leftLossCount;
-    let rightLossCount;
-    leftLossCount = Math.floor((data[0].values[0][0] * 1000 - start) / step);
-    res.unshift(...Array(leftLossCount).fill(-1));
-    rightLossCount = Math.floor(
-      (end - data[0].values[data[0].values.length - 1][0] * 1000) / step
-    );
-    res.push(...Array(rightLossCount).fill(-2));
-    return res;
+  async getInsertVectorsCount(start: number, end: number, step: number) {
+    const expr = `${totalVectorsCountMetric}${PrometheusService.selector}`;
+    const data = await this.queryRange(expr, start, end, step);
+    return data.length > 0 ? data[0].values : [];
   }
-  getInsertVectorsCount = (start: number, end: number, step: number) =>
-    this.getVectorsCount(totalVectorsCountMetric, start, end, step);
 
   async getSearchVectorsCount(start: number, end: number, step: number) {
-    const expr = `rate(${searchVectorsCountMetric}${
+    const expr = `delta(${searchVectorsCountMetric}${
       PrometheusService.selector
     }[${step / 1000}s])`;
-    const result = await this.queryRange(expr, start, end, step);
-    const data = result.data.result;
-    const length = Math.floor((end - start) / step);
-
-    if (data.length === 0) return Array(length).fill(0);
-
-    const res = data[0].values.map((d: any) => +d[1]);
-
-    let leftLossCount;
-    let rightLossCount;
-    leftLossCount = Math.floor((data[0].values[0][0] * 1000 - start) / step);
-    res.unshift(...Array(leftLossCount).fill(-1));
-    rightLossCount = Math.floor(
-      (end - data[0].values[data[0].values.length - 1][0] * 1000) / step
-    );
-    res.push(...Array(rightLossCount).fill(-2));
-    return res;
+    const data = await this.queryRange(expr, start, end, step);
+    return data.length > 0
+      ? data[0].values.map((d: number) => Math.round(d))
+      : [];
   }
 
   async getSQLatency(start: number, end: number, step: number) {
@@ -178,23 +147,8 @@ export class PrometheusService {
       `(rate(${sqLatencyMetric}${PrometheusService.selector}[${
         step / 1000
       }s])))`;
-    const result = await this.queryRange(expr, start, end, step);
-    const data = result.data.result;
-
-    const length = Math.floor((end - start) / step);
-    if (data.length === 0) return Array(length).fill(0);
-
-    const res = data[0].values.map((d: any) => (isNaN(d[1]) ? 0 : +d[1]));
-    // .slice(1);
-    let leftLossCount;
-    let rightLossCount;
-    leftLossCount = Math.floor((data[0].values[0][0] * 1000 - start) / step);
-    res.unshift(...Array(leftLossCount).fill(-1));
-    rightLossCount = Math.floor(
-      (end - data[0].values[data[0].values.length - 1][0] * 1000) / step
-    );
-    res.push(...Array(rightLossCount).fill(-2));
-    return res;
+    const data = await this.queryRange(expr, start, end, step);
+    return data.length > 0 ? data[0].values : [];
   }
 
   async getThirdPartyServiceHealthStatus(
@@ -203,50 +157,36 @@ export class PrometheusService {
     end: number,
     step: number
   ) {
-    const expr = `sum by (status) (${metricName}${PrometheusService.selector})`;
-    const result = await this.queryRange(expr, start, end, step);
-    const data = result.data.result;
-    const length = Math.floor((end - start) / step);
-    const totalCount = data
-      .find((d: any) => d.metric.status === 'total')
-      .values.map((d: any) => +d[1]);
-    const totalSlices = totalCount
-      .map((d: number, i: number) => (i > 0 ? d - totalCount[i - 1] : d))
-      .slice(1);
-    const successCount = data
-      .find((d: any) => d.metric.status === 'success')
-      .values.map((d: any) => +d[1]);
-    const successSlices = successCount
-      .map((d: number, i: number) => (i > 0 ? d - successCount[i - 1] : d))
-      .slice(1);
-    const res = totalSlices.map((d: number, i: number) =>
-      d === 0 ? 1 : successSlices[i] / d
+    const expr = `sum by (status) (delta(${metricName}${
+      PrometheusService.selector
+    }[${step / 1000}s]))`;
+    const data = await this.queryRange(expr, start, end, step);
+
+    const totalData =
+      data.find((d: any) => d.metric.status === 'total')?.values || [];
+    const successData =
+      data.find((d: any) => d.metric.status === 'success')?.values || [];
+    return totalData.map((d: number, i: number) =>
+      d < 0 ? d : d === 0 ? 1 : successData[i] / d
     );
-    res.unshift(...Array(length - res.length).fill(-1));
-    return res;
   }
 
   async getInternalNodesCPUData(start: number, end: number, step: number) {
     const expr = `rate(${cpuMetric}${PrometheusService.selector}[${
       step / 1000
     }s])`;
-    const result = await this.queryRange(expr, start, end, step);
-    return result.data.result;
+    return await this.queryRange(expr, start, end, step);
   }
 
   async getInternalNodesMemoryData(start: number, end: number, step: number) {
     const expr = `${memoryMetric}${PrometheusService.selector}`;
-    const result = await this.queryRange(expr, start, end, step);
-    return result.data.result;
+    return await this.queryRange(expr, start, end, step);
   }
 
   reconstructNodeData(
     cpuNodesData: any,
     memoryNodesData: any,
-    type: string,
-    start: number,
-    end: number,
-    step: number
+    type: string
   ): IPrometheusNode[] {
     const cpuNodes = cpuNodesData.filter(
       (d: any) => d.metric.container.indexOf(type) >= 0
@@ -258,32 +198,16 @@ export class PrometheusService {
       const nodeType =
         d.metric.container.indexOf('coord') >= 0 ? 'coord' : 'node';
       const pod = d.metric.pod;
-      const cpu = d.values.map((v: any) => +v[1]);
-
-      let leftLossCount;
-      let rightLossCount;
-      leftLossCount = Math.floor((d.values[0][0] * 1000 - start) / step);
-      cpu.unshift(...Array(leftLossCount).fill(-1));
-      rightLossCount = Math.floor(
-        (end - d.values[d.values.length - 1][0] * 1000) / step
-      );
-      cpu.push(...Array(rightLossCount).fill(-2));
+      const cpu = d?.values || [];
 
       const node = memoryNodes.find((data: any) => data.metric.pod === pod);
-      const memory = node.values.map((v: any) => +v[1]);
-
-      leftLossCount = Math.floor((node.values[0][0] * 1000 - start) / step);
-      memory.unshift(...Array(leftLossCount).fill(-1));
-      rightLossCount = Math.floor(
-        (end - node.values[node.values.length - 1][0] * 1000) / step
-      );
-      memory.push(...Array(rightLossCount).fill(-2));
+      const memory = node ? node?.values || [] : [];
 
       return {
         type: nodeType,
         pod,
-        cpu: cpu.slice(1),
-        memory: memory.slice(1),
+        cpu,
+        memory,
       } as IPrometheusNode;
     });
 
@@ -291,8 +215,10 @@ export class PrometheusService {
   }
 
   async getInternalNodesData(start: number, end: number, step: number) {
-    const cpuNodes = await this.getInternalNodesCPUData(start, end, step);
-    const memoryNodes = await this.getInternalNodesMemoryData(start, end, step);
+    const [cpuNodes, memoryNodes] = await Promise.all([
+      this.getInternalNodesCPUData(start, end, step),
+      this.getInternalNodesMemoryData(start, end, step),
+    ]);
 
     const [rootNodes, queryNodes, indexNodes, dataNodes] = [
       'root',
@@ -300,7 +226,7 @@ export class PrometheusService {
       'index',
       'data',
     ].map((metric: string) =>
-      this.reconstructNodeData(cpuNodes, memoryNodes, metric, start, end, step)
+      this.reconstructNodeData(cpuNodes, memoryNodes, metric)
     );
     return { rootNodes, queryNodes, indexNodes, dataNodes };
   }
@@ -315,41 +241,26 @@ export class PrometheusService {
     step: number;
   }) {
     if (!PrometheusService.isReady) {
-      return {
-
-      }
+      return {};
     }
-    const meta = await this.getThirdPartyServiceHealthStatus(
-      metaMetric,
-      start,
-      end,
-      step
-    );
-    const msgstream = await this.getThirdPartyServiceHealthStatus(
-      msgstreamMetric,
-      start,
-      end,
-      step
-    );
-    const objstorage = await this.getThirdPartyServiceHealthStatus(
-      objstorageMetric,
-      start,
-      end,
-      step
-    );
-    const totalVectorsCount = await this.getInsertVectorsCount(
-      start,
-      end,
-      step
-    );
-    const searchVectorsCount = await this.getSearchVectorsCount(
-      start,
-      end,
-      step
-    );
-    const sqLatency = await this.getSQLatency(start, end, step);
-    const { rootNodes, queryNodes, indexNodes, dataNodes } =
-      await this.getInternalNodesData(start, end, step);
+
+    const [
+      meta,
+      msgstream,
+      objstorage,
+      totalVectorsCount,
+      searchVectorsCount,
+      sqLatency,
+      { rootNodes, queryNodes, indexNodes, dataNodes },
+    ] = await Promise.all([
+      this.getThirdPartyServiceHealthStatus(metaMetric, start, end, step),
+      this.getThirdPartyServiceHealthStatus(msgstreamMetric, start, end, step),
+      this.getThirdPartyServiceHealthStatus(objstorageMetric, start, end, step),
+      this.getInsertVectorsCount(start, end, step),
+      this.getSearchVectorsCount(start, end, step),
+      this.getSQLatency(start, end, step),
+      this.getInternalNodesData(start, end, step),
+    ]);
 
     return {
       totalVectorsCount,