From 30f414925cac88d14f06f65e4a43b1939941472f Mon Sep 17 00:00:00 2001 From: Jayesh Choudhary Date: Thu, 24 Oct 2024 22:39:45 +0530 Subject: [PATCH] fix(ui): remove redundant api calls in home page (#14295) --- .../app/components/AsyncInstanceTable.tsx | 85 ++++++------------- .../components/Homepage/InstancesTables.tsx | 8 +- .../src/main/resources/app/pages/HomePage.tsx | 23 +++-- .../app/pages/InstanceListingPage.tsx | 19 ++++- .../src/main/resources/app/pages/Tenants.tsx | 22 ++++- .../resources/app/utils/PinotMethodUtils.ts | 32 +++---- 6 files changed, 101 insertions(+), 88 deletions(-) diff --git a/pinot-controller/src/main/resources/app/components/AsyncInstanceTable.tsx b/pinot-controller/src/main/resources/app/components/AsyncInstanceTable.tsx index 67a0a08841cb..12d6b94a0ce6 100644 --- a/pinot-controller/src/main/resources/app/components/AsyncInstanceTable.tsx +++ b/pinot-controller/src/main/resources/app/components/AsyncInstanceTable.tsx @@ -19,7 +19,7 @@ import React, { useState, useEffect } from 'react'; import { get, lowerCase, mapKeys, startCase } from 'lodash'; -import { InstanceType, TableData } from 'Models'; +import { DataTable, InstanceType, TableData } from 'Models'; import CustomizedTables from './Table'; import PinotMethodUtils from '../utils/PinotMethodUtils'; import Utils from '../utils/Utils'; @@ -28,6 +28,8 @@ import Loading from './Loading'; type BaseProps = { instanceType: InstanceType; showInstanceDetails?: boolean; + instanceNames: string[] | null; + liveInstanceNames?: string[]; }; type ClusterProps = BaseProps & { @@ -45,7 +47,8 @@ type Props = ClusterProps | TenantProps; export const AsyncInstanceTable = ({ instanceType, cluster, - tenant, + instanceNames, + liveInstanceNames, showInstanceDetails = false, }: Props) => { const instanceColumns = showInstanceDetails @@ -55,68 +58,30 @@ export const AsyncInstanceTable = ({ Utils.getLoadingTableData(instanceColumns) ); - const fetchInstances = async ( - instanceType: InstanceType, - tenant?: string - ): Promise => { - if (tenant) { - if (instanceType === InstanceType.BROKER) { - return PinotMethodUtils.getBrokerOfTenant(tenant).then( - (brokersData) => { - return Array.isArray(brokersData) ? brokersData : []; - } - ); - } else if (instanceType === InstanceType.SERVER) { - return PinotMethodUtils.getServerOfTenant(tenant).then( - (serversData) => { - return Array.isArray(serversData) ? serversData : []; - } - ); - } - } else { - return fetchInstancesOfType(instanceType); + useEffect(() => { + if(instanceNames) { + const loadingColumns = Array(instanceColumns.length - 1).fill(Loading); + setInstanceData({ + columns: instanceColumns, + records: instanceNames.map((name) => [name, ...loadingColumns]) || [], + }); } - }; - - const fetchInstancesOfType = async (instanceType: InstanceType) => { - return PinotMethodUtils.getAllInstances().then((instancesData) => { - const lowercaseInstanceData = mapKeys(instancesData, (value, key) => - lowerCase(key) - ); - return get(lowercaseInstanceData, lowerCase(instanceType)); - }); - }; + }, [instanceNames]); useEffect(() => { - const instances = fetchInstances(instanceType, tenant); - if (showInstanceDetails && cluster.length > 0) { - const instanceDetails = instances.then(async (instancesData) => { - const liveInstanceArr = await PinotMethodUtils.getLiveInstance(cluster); - return PinotMethodUtils.getInstanceData( - instancesData, - liveInstanceArr.data - ); - }); - instanceDetails.then((instanceDetailsData) => { - setInstanceData(instanceDetailsData); - }); - } else if (showInstanceDetails && cluster.length == 0) { - instances.then((instancesData) => { - const defaultLoadingArray = Array(4).fill(Loading); - setInstanceData({ - columns: instanceColumns, - records: [[instancesData[0], ...defaultLoadingArray ]], - }); - }); - } else { - instances.then((instancesData) => { - setInstanceData({ - columns: instanceColumns, - records: instancesData.map((instance) => [instance]), - }); - }); + // async load all the other details + if(showInstanceDetails && cluster && instanceNames && liveInstanceNames) { + fetchAdditionalInstanceDetails(); } - }, [instanceType, cluster, tenant, showInstanceDetails]); + }, [showInstanceDetails, cluster, instanceNames, liveInstanceNames]); + + const fetchAdditionalInstanceDetails = async () => { + const additionalData = await PinotMethodUtils.getInstanceData( + instanceNames, + liveInstanceNames + ); + setInstanceData(additionalData); + } return ( { +const Instances = ({ clusterName, instanceType, instances, liveInstanceNames }: Props) => { const order = [ InstanceType.CONTROLLER, InstanceType.BROKER, @@ -46,6 +48,8 @@ const Instances = ({ clusterName, instanceType }: Props) => { cluster={clusterName} instanceType={key} showInstanceDetails + instanceNames={instances?.[key] || null} + liveInstanceNames={liveInstanceNames || null} /> ); })} diff --git a/pinot-controller/src/main/resources/app/pages/HomePage.tsx b/pinot-controller/src/main/resources/app/pages/HomePage.tsx index 73f16342cf27..e2909b61a851 100644 --- a/pinot-controller/src/main/resources/app/pages/HomePage.tsx +++ b/pinot-controller/src/main/resources/app/pages/HomePage.tsx @@ -28,6 +28,7 @@ import ClusterConfig from '../components/Homepage/ClusterConfig'; import useTaskTypesTable from '../components/Homepage/useTaskTypesTable'; import Skeleton from '@material-ui/lab/Skeleton'; import { getTenants } from '../requests'; +import { DataTable, InstanceType } from 'Models'; const useStyles = makeStyles((theme) => ({ paper: { @@ -82,8 +83,8 @@ const HomePage = () => { const [brokerCount, setBrokerCount] = useState(0); const [serverCount, setServerCount] = useState(0); const [minionCount, setMinionCount] = useState(0); - // const [instances, setInstances] = useState(); - + const [instances, setInstances] = useState(); + const [liveInstanceNames, setLiveInstanceNames] = useState(); const [fetchingTables, setFetchingTables] = useState(true); const [tablesCount, setTablesCount] = useState(0); @@ -91,10 +92,11 @@ const HomePage = () => { const fetchData = async () => { PinotMethodUtils.getAllInstances().then((res) => { - setControllerCount(get(res, 'Controller', []).length); - setBrokerCount(get(res, 'Broker', []).length); - setServerCount(get(res, 'Server', []).length); - setMinionCount(get(res, 'Minion', []).length); + setControllerCount(res[InstanceType.CONTROLLER].length); + setBrokerCount(res[InstanceType.BROKER].length); + setServerCount(res[InstanceType.SERVER].length); + setMinionCount(res[InstanceType.MINION].length); + setInstances(res); setFetchingInstances(false); }); @@ -114,6 +116,9 @@ const HomePage = () => { fetchClusterName().then((clusterNameRes) => { setClusterName(clusterNameRes); + PinotMethodUtils.getLiveInstance(clusterNameRes).then((res) => { + setLiveInstanceNames(res.data || []); + }); }); }; @@ -203,7 +208,11 @@ const HomePage = () => { - + {taskTypesTable} diff --git a/pinot-controller/src/main/resources/app/pages/InstanceListingPage.tsx b/pinot-controller/src/main/resources/app/pages/InstanceListingPage.tsx index 9d0070725ce4..770142fdc104 100644 --- a/pinot-controller/src/main/resources/app/pages/InstanceListingPage.tsx +++ b/pinot-controller/src/main/resources/app/pages/InstanceListingPage.tsx @@ -19,8 +19,7 @@ import React, {useState, useEffect} from 'react'; import { Grid, makeStyles } from '@material-ui/core'; -import { startCase, pick } from 'lodash'; -import { DataTable, InstanceType } from 'Models'; +import { DataTable } from 'Models'; import AppLoader from '../components/AppLoader'; import PinotMethodUtils from '../utils/PinotMethodUtils'; import Instances from '../components/Homepage/InstancesTables'; @@ -40,6 +39,8 @@ const InstanceListingPage = () => { const [fetching, setFetching] = useState(true); const [clusterName, setClusterName] = useState(''); + const [instances, setInstances] = useState(); + const [liveInstanceNames, setLiveInstanceNames] = useState(); const fetchData = async () => { let clusterNameRes = localStorage.getItem('pinot_ui:clusterName'); @@ -47,6 +48,13 @@ const InstanceListingPage = () => { clusterNameRes = await PinotMethodUtils.getClusterName(); } setClusterName(clusterNameRes); + + const liveInstanceNames = await PinotMethodUtils.getLiveInstance(clusterNameRes); + setLiveInstanceNames(liveInstanceNames.data || []); + + const instancesList = await PinotMethodUtils.getAllInstances(); + setInstances(instancesList); + setFetching(false); }; @@ -60,7 +68,12 @@ const InstanceListingPage = () => { ) : ( - + ); }; diff --git a/pinot-controller/src/main/resources/app/pages/Tenants.tsx b/pinot-controller/src/main/resources/app/pages/Tenants.tsx index 19f61ec6e11e..e43c17c36b0e 100644 --- a/pinot-controller/src/main/resources/app/pages/Tenants.tsx +++ b/pinot-controller/src/main/resources/app/pages/Tenants.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Grid, makeStyles } from '@material-ui/core'; import { InstanceType } from 'Models'; import { RouteComponentProps } from 'react-router-dom'; @@ -25,6 +25,7 @@ import SimpleAccordion from '../components/SimpleAccordion'; import AsyncPinotTables from '../components/AsyncPinotTables'; import CustomButton from '../components/CustomButton'; import { AsyncInstanceTable } from '../components/AsyncInstanceTable'; +import PinotMethodUtils from '../utils/PinotMethodUtils'; const useStyles = makeStyles((theme) => ({ operationDiv: { @@ -41,6 +42,23 @@ type Props = { const TenantPage = ({ match }: RouteComponentProps) => { const { tenantName } = match.params; const classes = useStyles(); + const [instanceNames, setInstanceNames] = useState({ + [InstanceType.BROKER]: null, + [InstanceType.SERVER]: null, + }) + + useEffect(() => { + fetchInstanceData(); + }, []); + + const fetchInstanceData = async () => { + const brokerNames = await PinotMethodUtils.getBrokerOfTenant(tenantName) || []; + const serverNames = await PinotMethodUtils.getServerOfTenant(tenantName) || []; + setInstanceNames({ + [InstanceType.BROKER]: Array.isArray(brokerNames) ? brokerNames : [], + [InstanceType.SERVER]: Array.isArray(serverNames) ? serverNames : [], + }); + } return ( ) => { diff --git a/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts b/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts index 348751c36820..4207e59f4760 100644 --- a/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts +++ b/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts @@ -18,8 +18,8 @@ */ import jwtDecode from "jwt-decode"; -import { get, map, each, isEqual, isArray, keys, union } from 'lodash'; -import { DataTable, SchemaInfo, SegmentMetadata, SqlException, SQLResult, TableSize } from 'Models'; +import { get, each, isEqual, isArray, keys, union } from 'lodash'; +import { DataTable, InstanceType, SchemaInfo, SegmentMetadata, SqlException, SQLResult } from 'Models'; import moment from 'moment'; import { getTenants, @@ -33,7 +33,6 @@ import { getTaskTypes, getTaskTypeDebug, getTables, - getTaskTypeTasks, getTaskTypeTasksCount, getTaskTypeState, stopTasks, @@ -101,7 +100,7 @@ import { getServerToSegmentsCount } from '../requests'; import { baseApi } from './axios-config'; -import Utils, { getDisplaySegmentStatus } from './Utils'; +import Utils from './Utils'; import { matchPath } from 'react-router'; import RouterData from '../router'; const JSONbig = require('json-bigint')({'storeAsString': true}) @@ -147,19 +146,22 @@ const getTenantsData = () => { // This method is used to fetch all instances on cluster manager home page // API: /instances -// Expected Output: {Controller: ['Controller1', 'Controller2'], Broker: ['Broker1', 'Broker2']} +// Expected Output: {CONTROLLER: ['Controller1', 'Controller2'], BROKER: ['Broker1', 'Broker2']} const getAllInstances = () => { return getInstances().then(({ data }) => { - const initialVal: DataTable = {}; - // It will create instances list array like - // {Controller: ['Controller1', 'Controller2'], Broker: ['Broker1', 'Broker2']} - const groupedData = data.instances.reduce((r, a) => { - const y = a.split('_'); - const key = y[0].trim(); - r[key] = [...(r[key] || []), a]; - return r; - }, initialVal); - return {'Controller': groupedData.Controller, ...groupedData}; + const instanceTypeToInstancesMap: DataTable = { + [InstanceType.CONTROLLER]: [], + [InstanceType.BROKER]: [], + [InstanceType.SERVER]: [], + [InstanceType.MINION]: [] + }; + + data.instances.forEach((instance) => { + const instanceType = instance.split('_')[0].toUpperCase(); + instanceTypeToInstancesMap[instanceType].push(instance); + }); + + return instanceTypeToInstancesMap; }); };