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

jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new c662e43b5b UI Load time Improvement API Fixes #13278 (#13296)
c662e43b5b is described below

commit c662e43b5bbc2428a372d778048db6c4758e1fff
Author: Chaitanya Deepthi <45308220+deepthi...@users.noreply.github.com>
AuthorDate: Wed Aug 28 16:41:43 2024 -0700

    UI Load time Improvement API Fixes #13278 (#13296)
---
 .../api/resources/PinotSchemaRestletResource.java  |  32 ++++
 .../api/resources/PinotSegmentRestletResource.java |  26 ++-
 .../api/resources/PinotTaskRestletResource.java    |   9 +
 .../api/resources/SegmentStatusInfo.java           |  49 ++++++
 .../pinot/controller/api/resources/TableSize.java  |  15 +-
 .../pinot/controller/api/resources/TableViews.java |  84 ++++++++++
 .../helix/core/PinotHelixResourceManager.java      |  20 +++
 .../src/main/resources/app/interfaces/types.d.ts   |  17 ++
 .../src/main/resources/app/pages/TenantDetails.tsx |  21 +--
 .../src/main/resources/app/requests/index.ts       |  21 ++-
 .../main/resources/app/utils/PinotMethodUtils.ts   |  82 ++++++----
 .../controller/helix/SegmentStatusCheckerTest.java | 181 +++++++++++++++++++++
 .../java/org/apache/pinot/core/auth/Actions.java   |   3 +
 .../java/org/apache/pinot/spi/data/SchemaInfo.java |  70 ++++++++
 .../apache/pinot/spi/utils/CommonConstants.java    |   6 +
 .../org/apache/pinot/spi/data/SchemaInfoTest.java  |  78 +++++++++
 16 files changed, 656 insertions(+), 58 deletions(-)

diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSchemaRestletResource.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSchemaRestletResource.java
index 4b2c1e33c9..eecfe63b6c 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSchemaRestletResource.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSchemaRestletResource.java
@@ -31,6 +31,8 @@ import io.swagger.annotations.SecurityDefinition;
 import io.swagger.annotations.SwaggerDefinition;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import javax.inject.Inject;
@@ -50,6 +52,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.pinot.common.config.provider.TableCache;
 import org.apache.pinot.common.exception.SchemaAlreadyExistsException;
 import org.apache.pinot.common.exception.SchemaBackwardIncompatibleException;
 import org.apache.pinot.common.exception.SchemaNotFoundException;
@@ -72,6 +75,7 @@ import org.apache.pinot.segment.local.utils.SchemaUtils;
 import org.apache.pinot.spi.config.table.TableConfig;
 import org.apache.pinot.spi.data.FieldSpec;
 import org.apache.pinot.spi.data.Schema;
+import org.apache.pinot.spi.data.SchemaInfo;
 import org.apache.pinot.spi.utils.JsonUtils;
 import org.apache.pinot.spi.utils.builder.TableNameBuilder;
 import org.glassfish.grizzly.http.server.Request;
@@ -118,6 +122,34 @@ public class PinotSchemaRestletResource {
     return 
_pinotHelixResourceManager.getSchemaNames(headers.getHeaderString(DATABASE));
   }
 
+  @GET
+  @Produces(MediaType.APPLICATION_JSON)
+  @Path("/schemas/info")
+  @Authorize(targetType = TargetType.CLUSTER, action = 
Actions.Cluster.GET_SCHEMAS_INFO)
+  @ApiOperation(value = "List all schemas info with count of field specs", 
notes = "Lists all schemas with field "
+      + "count details")
+  public String getSchemasInfo(@Context HttpHeaders headers)
+      throws JsonProcessingException {
+    Map<String, Object> schemaInfoObjects = new LinkedHashMap<>();
+    List<SchemaInfo> schemasInfo = new ArrayList<>();
+    List<String> uncachedSchemas = new ArrayList<>();
+    TableCache cache = _pinotHelixResourceManager.getTableCache();
+    List<String> schemas = 
_pinotHelixResourceManager.getSchemaNames(headers.getHeaderString(DATABASE));
+    for (String schemaName : schemas) {
+      Schema schema = cache.getSchema(schemaName);
+      if (schema != null) {
+        schemasInfo.add(new SchemaInfo(schema));
+      } else {
+        uncachedSchemas.add(schemaName);
+      }
+    }
+    schemaInfoObjects.put("schemasInfo", schemasInfo);
+    if (!uncachedSchemas.isEmpty()) {
+      schemaInfoObjects.put("uncachedSchemas", uncachedSchemas);
+    }
+    return JsonUtils.objectToPrettyString(schemaInfoObjects);
+  }
+
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @Path("/schemas/{schemaName}")
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSegmentRestletResource.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSegmentRestletResource.java
index 74c09d8da7..2f0d565590 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSegmentRestletResource.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSegmentRestletResource.java
@@ -237,21 +237,37 @@ public class PinotSegmentRestletResource {
       notes = "Get a map from server to segments hosted by the server")
   public List<Map<String, Object>> getServerToSegmentsMap(
       @ApiParam(value = "Name of the table", required = true) 
@PathParam("tableName") String tableName,
-      @ApiParam(value = "OFFLINE|REALTIME") @QueryParam("type") String 
tableTypeStr, @Context HttpHeaders headers) {
+      @ApiParam(value = "OFFLINE|REALTIME") @QueryParam("type") String 
tableTypeStr,
+      @QueryParam("verbose") @DefaultValue("true") boolean verbose, @Context 
HttpHeaders headers) {
     tableName = DatabaseUtils.translateTableName(tableName, headers);
-    List<String> tableNamesWithType = ResourceUtils
-        .getExistingTableNamesWithType(_pinotHelixResourceManager, tableName, 
Constants.validateTableType(tableTypeStr),
-            LOGGER);
+    List<String> tableNamesWithType = 
ResourceUtils.getExistingTableNamesWithType(_pinotHelixResourceManager, 
tableName,
+        Constants.validateTableType(tableTypeStr), LOGGER);
     List<Map<String, Object>> resultList = new 
ArrayList<>(tableNamesWithType.size());
     for (String tableNameWithType : tableNamesWithType) {
       Map<String, Object> resultForTable = new LinkedHashMap<>();
       resultForTable.put("tableName", tableNameWithType);
-      resultForTable.put("serverToSegmentsMap", 
_pinotHelixResourceManager.getServerToSegmentsMap(tableNameWithType));
+      if (!verbose) {
+        resultForTable.put("serverToSegmentsCountMap",
+            
_pinotHelixResourceManager.getServerToSegmentsCountMap(tableNameWithType));
+      } else {
+        Map<String, List<String>> serverToSegmentsMap =
+            
_pinotHelixResourceManager.getServerToSegmentsMap(tableNameWithType);
+        resultForTable.put("serverToSegmentsMap", serverToSegmentsMap);
+        resultForTable.put("serverToSegmentsCountMap", 
getServerToSegmentCountMap(serverToSegmentsMap));
+      }
       resultList.add(resultForTable);
     }
     return resultList;
   }
 
+  private Map<String, Integer> getServerToSegmentCountMap(Map<String, 
List<String>> serverToSegmentsMap) {
+    Map<String, Integer> serverToSegmentCount = new TreeMap<>();
+    for (Map.Entry<String, List<String>> entry : 
serverToSegmentsMap.entrySet()) {
+      serverToSegmentCount.put(entry.getKey(), entry.getValue().size());
+    }
+    return serverToSegmentCount;
+  }
+
   @GET
   @Path("segments/{tableName}/lineage")
   @Authorize(targetType = TargetType.TABLE, paramName = "tableName", action = 
Actions.Table.GET_SEGMENT_LINEAGE)
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotTaskRestletResource.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotTaskRestletResource.java
index bc02a82ff5..9cfcbc3a06 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotTaskRestletResource.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotTaskRestletResource.java
@@ -200,6 +200,15 @@ public class PinotTaskRestletResource {
     return tasks;
   }
 
+  @GET
+  @Path("/tasks/{taskType}/tasks/count")
+  @Authorize(targetType = TargetType.CLUSTER, action = 
Actions.Cluster.GET_TASK_COUNT)
+  @Produces(MediaType.APPLICATION_JSON)
+  @ApiOperation("Count of all tasks for the given task type")
+  public int getTasksCount(@ApiParam(value = "Task type", required = true) 
@PathParam("taskType") String taskType) {
+    return _pinotHelixTaskResourceManager.getTasks(taskType).size();
+  }
+
   @GET
   @Path("/tasks/{taskType}/{tableNameWithType}/state")
   @Authorize(targetType = TargetType.CLUSTER, action = 
Actions.Cluster.GET_TASK)
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/SegmentStatusInfo.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/SegmentStatusInfo.java
new file mode 100644
index 0000000000..613a744570
--- /dev/null
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/SegmentStatusInfo.java
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.controller.api.resources;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * This class gives the details of a particular segment and it's status
+ *
+ */
+public class SegmentStatusInfo {
+  @JsonProperty("segmentName")
+  String _segmentName;
+
+  @JsonProperty("segmentStatus")
+  String _segmentStatus;
+
+  public String getSegmentName() {
+    return _segmentName;
+  }
+
+  public String getSegmentStatus() {
+    return _segmentStatus;
+  }
+
+  @JsonCreator
+  public SegmentStatusInfo(@JsonProperty("segmentName") String segmentName,
+      @JsonProperty("segmentStatus") String segmentStatus) {
+    _segmentName = segmentName;
+    _segmentStatus = segmentStatus;
+  }
+}
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/TableSize.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/TableSize.java
index ea3671b083..ede9d3eb73 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/TableSize.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/TableSize.java
@@ -27,11 +27,14 @@ import io.swagger.annotations.ApiResponses;
 import io.swagger.annotations.Authorization;
 import io.swagger.annotations.SecurityDefinition;
 import io.swagger.annotations.SwaggerDefinition;
+import java.util.HashMap;
 import javax.inject.Inject;
+import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
@@ -80,12 +83,22 @@ public class TableSize {
   })
   public TableSizeReader.TableSizeDetails getTableSize(
       @ApiParam(value = "Table name without type", required = true, example = 
"myTable | myTable_OFFLINE")
-      @PathParam("tableName") String tableName, @Context HttpHeaders headers) {
+      @PathParam("tableName") String tableName,
+      @ApiParam(value = "Provide detailed information") @DefaultValue("true") 
@QueryParam("verbose") boolean verbose,
+      @Context HttpHeaders headers) {
     tableName = DatabaseUtils.translateTableName(tableName, headers);
     TableSizeReader.TableSizeDetails tableSizeDetails = null;
     try {
       tableSizeDetails =
           _tableSizeReader.getTableSizeDetails(tableName, 
_controllerConf.getServerAdminRequestTimeoutSeconds() * 1000);
+      if (!verbose) {
+        if (tableSizeDetails._offlineSegments != null) {
+          tableSizeDetails._offlineSegments._segments = new HashMap<>();
+        }
+        if (tableSizeDetails._realtimeSegments != null) {
+          tableSizeDetails._realtimeSegments._segments = new HashMap<>();
+        }
+      }
     } catch (Throwable t) {
       throw new ControllerApplicationException(LOGGER, String.format("Failed 
to read table size for %s", tableName),
           Response.Status.INTERNAL_SERVER_ERROR, t);
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/TableViews.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/TableViews.java
index a7b33ef133..c4ade0c947 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/TableViews.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/TableViews.java
@@ -19,6 +19,7 @@
 package org.apache.pinot.controller.api.resources;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiKeyAuthDefinition;
 import io.swagger.annotations.ApiOperation;
@@ -26,6 +27,9 @@ import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.Authorization;
 import io.swagger.annotations.SecurityDefinition;
 import io.swagger.annotations.SwaggerDefinition;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -48,6 +52,8 @@ import org.apache.pinot.core.auth.Actions;
 import org.apache.pinot.core.auth.Authorize;
 import org.apache.pinot.core.auth.TargetType;
 import org.apache.pinot.spi.config.table.TableType;
+import org.apache.pinot.spi.utils.CommonConstants;
+import org.apache.pinot.spi.utils.JsonUtils;
 import org.apache.pinot.spi.utils.builder.TableNameBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -110,6 +116,84 @@ public class TableViews {
     return getTableState(tableName, EXTERNALVIEW, tableType);
   }
 
+  @GET
+  @Produces(MediaType.APPLICATION_JSON)
+  @Path("/tables/{tableName}/segmentsStatus")
+  @Authorize(targetType = TargetType.TABLE, paramName = "tableName", action = 
Actions.Table.GET_SEGMENT_STATUS)
+  @ApiOperation(value = "Get segment names to segment status map", notes = 
"Get segment statuses of each segment")
+  public String getSegmentsStatusDetails(
+      @ApiParam(value = "Name of the table", required = true) 
@PathParam("tableName") String tableName,
+      @ApiParam(value = "realtime|offline", required = false) 
@QueryParam("tableType") String tableTypeStr,
+      @Context HttpHeaders headers)
+      throws JsonProcessingException {
+    tableName = DatabaseUtils.translateTableName(tableName, headers);
+    TableType tableType = validateTableType(tableTypeStr);
+    TableViews.TableView externalView = getTableState(tableName, 
TableViews.EXTERNALVIEW, tableType);
+    TableViews.TableView idealStateView = getTableState(tableName, 
TableViews.IDEALSTATE, tableType);
+    List<SegmentStatusInfo> segmentStatusInfoListMap = new ArrayList<>();
+    segmentStatusInfoListMap = getSegmentStatuses(externalView, 
idealStateView);
+    return JsonUtils.objectToPrettyString(segmentStatusInfoListMap);
+  }
+
+  public List<SegmentStatusInfo> getSegmentStatuses(TableViews.TableView 
externalView,
+      TableViews.TableView idealStateView) {
+    Map<String, Map<String, String>> idealStateMap = 
getStateMap(idealStateView);
+    Map<String, Map<String, String>> externalViewMap = 
getStateMap(externalView);
+    List<SegmentStatusInfo> segmentStatusInfoList = new ArrayList<>();
+
+    for (Map.Entry<String, Map<String, String>> entry : 
externalViewMap.entrySet()) {
+      String segment = entry.getKey();
+      Map<String, String> externalViewEntryValue = entry.getValue();
+      Map<String, String> idealViewEntryValue = idealStateMap.get(segment);
+      if (isErrorSegment(externalViewEntryValue)) {
+        segmentStatusInfoList.add(
+            new SegmentStatusInfo(segment, 
CommonConstants.Helix.StateModel.DisplaySegmentStatus.BAD));
+      } else {
+        boolean isViewsEqual = 
externalViewEntryValue.equals(idealViewEntryValue);
+        if (isViewsEqual) {
+          if (isOnlineOrConsumingSegment(externalViewEntryValue)) {
+            segmentStatusInfoList.add(
+                new SegmentStatusInfo(segment, 
CommonConstants.Helix.StateModel.DisplaySegmentStatus.GOOD));
+          } else if (isOfflineSegment(externalViewEntryValue)) {
+            segmentStatusInfoList.add(
+                new SegmentStatusInfo(segment, 
CommonConstants.Helix.StateModel.DisplaySegmentStatus.GOOD));
+          } else {
+            segmentStatusInfoList.add(
+                new SegmentStatusInfo(segment, 
CommonConstants.Helix.StateModel.DisplaySegmentStatus.UPDATING));
+          }
+        } else {
+          segmentStatusInfoList.add(
+              new SegmentStatusInfo(segment, 
CommonConstants.Helix.StateModel.DisplaySegmentStatus.UPDATING));
+        }
+      }
+    }
+    return segmentStatusInfoList;
+  }
+
+  private Map<String, Map<String, String>> getStateMap(TableViews.TableView 
view) {
+    if (view != null && view._offline != null && !view._offline.isEmpty()) {
+      return view._offline;
+    } else if (view != null && view._realtime != null && 
!view._realtime.isEmpty()) {
+      return view._realtime;
+    } else {
+      return new HashMap<>();
+    }
+  }
+
+  private boolean isErrorSegment(Map<String, String> stateMap) {
+    return 
stateMap.values().contains(CommonConstants.Helix.StateModel.SegmentStateModel.ERROR);
+  }
+
+  private boolean isOnlineOrConsumingSegment(Map<String, String> stateMap) {
+    return stateMap.values().stream().allMatch(
+        state -> 
state.equals(CommonConstants.Helix.StateModel.SegmentStateModel.CONSUMING) || 
state.equals(
+            CommonConstants.Helix.StateModel.SegmentStateModel.ONLINE));
+  }
+
+  private boolean isOfflineSegment(Map<String, String> stateMap) {
+    return 
stateMap.values().contains(CommonConstants.Helix.StateModel.SegmentStateModel.OFFLINE);
+  }
+
   // we use name "view" to closely match underlying names and to not
   // confuse with table state of enable/disable
   private TableView getTableState(String tableName, String view, TableType 
tableType) {
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java
index 2171d6a70e..e8122b5485 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/PinotHelixResourceManager.java
@@ -2865,6 +2865,26 @@ public class PinotHelixResourceManager {
     return serverToSegmentsMap;
   }
 
+  /**
+   * Returns a map from server instance to count of segments it serves for the 
given table. Ignore OFFLINE segments from
+   * the ideal state because they are not supposed to be served.
+   */
+  public Map<String, Integer> getServerToSegmentsCountMap(String 
tableNameWithType) {
+    Map<String, Integer> serverToSegmentCountMap = new TreeMap<>();
+    IdealState idealState = 
_helixAdmin.getResourceIdealState(_helixClusterName, tableNameWithType);
+    if (idealState == null) {
+      throw new IllegalStateException("Ideal state does not exist for table: " 
+ tableNameWithType);
+    }
+    for (Map.Entry<String, Map<String, String>> entry : 
idealState.getRecord().getMapFields().entrySet()) {
+      for (Map.Entry<String, String> instanceStateEntry : 
entry.getValue().entrySet()) {
+        if (!instanceStateEntry.getValue().equals(SegmentStateModel.OFFLINE)) {
+          serverToSegmentCountMap.merge(instanceStateEntry.getKey(), 1, 
Integer::sum);
+        }
+      }
+    }
+    return serverToSegmentCountMap;
+  }
+
   /**
    * Returns a set of server instances for a given table and segment. Ignore 
OFFLINE segments from the ideal state
    * because they are not supposed to be served.
diff --git a/pinot-controller/src/main/resources/app/interfaces/types.d.ts 
b/pinot-controller/src/main/resources/app/interfaces/types.d.ts
index 52f937bf0a..084f139518 100644
--- a/pinot-controller/src/main/resources/app/interfaces/types.d.ts
+++ b/pinot-controller/src/main/resources/app/interfaces/types.d.ts
@@ -110,6 +110,16 @@ declare module 'Models' {
     error?: string;
   };
 
+  export type ServerToSegmentsCount = {
+    tableName: string;
+    serverToSegmentsCountMap: number;
+  };
+
+  export type SegmentStatusInfo = {
+    segmentName: string;
+    segmentStatus: DISPLAY_SEGMENT_STATUS;
+  };
+
   export type QueryTables = {
     tables: Array<string>;
   };
@@ -130,6 +140,13 @@ declare module 'Models' {
     fieldType?: string
   };
 
+  export type SchemaInfo = {
+    schemaName: string
+    numDimensionFields: number
+    numDateTimeFields: number
+    numMetricFields: number
+  };
+
   export type SQLResult = {
     resultTable: {
       dataSchema: {
diff --git a/pinot-controller/src/main/resources/app/pages/TenantDetails.tsx 
b/pinot-controller/src/main/resources/app/pages/TenantDetails.tsx
index 0fbd9c6af4..a761f15fba 100644
--- a/pinot-controller/src/main/resources/app/pages/TenantDetails.tsx
+++ b/pinot-controller/src/main/resources/app/pages/TenantDetails.tsx
@@ -186,24 +186,11 @@ const TenantPageDetails = ({ match }: 
RouteComponentProps<Props>) => {
 
   const fetchSegmentData = async () => {
     const result = await PinotMethodUtils.getSegmentList(tableName);
-    const {columns, records, externalViewObj} = result;
-    const instanceObj = {};
-    externalViewObj && Object.keys(externalViewObj).map((segmentName)=>{
-      const instanceKeys = Object.keys(externalViewObj[segmentName]);
-      instanceKeys.map((instanceName)=>{
-        if(!instanceObj[instanceName]){
-          instanceObj[instanceName] = 0;
-        }
-        instanceObj[instanceName] += 1;
-      })
-    });
-    const instanceRecords = [];
-    Object.keys(instanceObj).map((instanceName)=>{
-      instanceRecords.push([instanceName, instanceObj[instanceName]]);
-    })
+    const data = await 
PinotMethodUtils.fetchServerToSegmentsCountData(tableName, tableType);
+    const {columns, records} = result;
     setInstanceCountData({
       columns: instanceColumns,
-      records: instanceRecords
+      records: data.records
     });
 
     const segmentTableRows = [];
@@ -221,10 +208,10 @@ const TenantPageDetails = ({ match }: 
RouteComponentProps<Props>) => {
         },
       ])
     );
-
     setSegmentList({columns, records: segmentTableRows});
   };
 
+
   const fetchTableSchema = async () => {
     const result = await PinotMethodUtils.getTableSchemaData(tableName);
     if(result.error){
diff --git a/pinot-controller/src/main/resources/app/requests/index.ts 
b/pinot-controller/src/main/resources/app/requests/index.ts
index e2d4d54910..cb31fac76b 100644
--- a/pinot-controller/src/main/resources/app/requests/index.ts
+++ b/pinot-controller/src/main/resources/app/requests/index.ts
@@ -46,6 +46,9 @@ import {
   QuerySchemas,
   TableType,
   InstanceState, SegmentMetadata,
+  SchemaInfo,
+  SegmentStatusInfo,
+  ServerToSegmentsCount
 } from 'Models';
 
 const headers = {
@@ -73,6 +76,9 @@ export const putTable = (name: string, params: string): 
Promise<AxiosResponse<Op
 export const getSchemaList = (): Promise<AxiosResponse<QuerySchemas>> =>
   baseApi.get(`/schemas`);
 
+export const getSchemaInfo = (): Promise<AxiosResponse<OperationResponse>> =>
+  baseApi.get(`/schemas/info`);
+
 export const getSchema = (name: string): 
Promise<AxiosResponse<OperationResponse>> =>
   baseApi.get(`/schemas/${name}`);
 
@@ -89,8 +95,8 @@ export const putSchema = (name: string, params: string, 
reload?: boolean): Promi
 export const getSegmentMetadata = (tableName: string, segmentName: string): 
Promise<AxiosResponse<SegmentMetadata>> =>
   baseApi.get(`/segments/${tableName}/${segmentName}/metadata?columns=*`);
 
-export const getTableSize = (name: string): Promise<AxiosResponse<TableSize>> 
=>
-  baseApi.get(`/tables/${name}/size`);
+export const getTableSize = (name: string, verbose: boolean = false): 
Promise<AxiosResponse<TableSize>> =>
+  baseApi.get(`/tables/${name}/size?verbose=${verbose}`);
 
 export const getIdealState = (name: string): 
Promise<AxiosResponse<IdealState>> =>
   baseApi.get(`/tables/${name}/idealstate`);
@@ -98,6 +104,12 @@ export const getIdealState = (name: string): 
Promise<AxiosResponse<IdealState>>
 export const getExternalView = (name: string): 
Promise<AxiosResponse<IdealState>> =>
   baseApi.get(`/tables/${name}/externalview`);
 
+export const getServerToSegmentsCount = (name: string, tableType: TableType, 
verbose: boolean = false): Promise<AxiosResponse<ServerToSegmentsCount[]>> =>
+  
baseApi.get(`/segments/${name}/servers?type=${tableType}&verbose=${verbose}`);
+
+export const getSegmentsStatus = (name: string): 
Promise<AxiosResponse<SegmentStatusInfo[]>> =>
+  baseApi.get(`/tables/${name}/segmentsStatus`);
+
 export const getInstances = (): Promise<AxiosResponse<Instances>> =>
   baseApi.get('/instances');
 
@@ -128,6 +140,9 @@ export const getTaskTypes = (): 
Promise<AxiosResponse<OperationResponse>> =>
 export const getTaskTypeTasks = (taskType: string): 
Promise<AxiosResponse<OperationResponse>> =>
   baseApi.get(`/tasks/${taskType}/tasks`, { headers: { ...headers, Accept: 
'application/json' } });
 
+export const getTaskTypeTasksCount = (taskType: string): 
Promise<AxiosResponse<number>> =>
+  baseApi.get(`/tasks/${taskType}/tasks/count`, { headers: { ...headers, 
Accept: 'application/json' } });
+
 export const getTaskTypeState = (taskType: string): 
Promise<AxiosResponse<OperationResponse>> =>
   baseApi.get(`/tasks/${taskType}/state`, { headers: { ...headers, Accept: 
'application/json' } });
 
@@ -294,4 +309,4 @@ export const requestDeleteUser = (userObject: UserObject): 
Promise<AxiosResponse
     
baseApi.delete(`/users/${userObject.username}?component=${userObject.component}`);
 
 export const requestUpdateUser = (userObject: UserObject, passwordChanged: 
boolean): Promise<AxiosResponse<any>> =>
-    
baseApi.put(`/users/${userObject.username}?component=${userObject.component}&passwordChanged=${passwordChanged}`,
 JSON.stringify(userObject), {headers});
\ No newline at end of file
+    
baseApi.put(`/users/${userObject.username}?component=${userObject.component}&passwordChanged=${passwordChanged}`,
 JSON.stringify(userObject), {headers});
diff --git a/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts 
b/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts
index 7d971036dd..d3af96ff69 100644
--- a/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts
+++ b/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts
@@ -34,6 +34,7 @@ import {
   getTaskTypeDebug,
   getTables,
   getTaskTypeTasks,
+  getTaskTypeTasksCount,
   getTaskTypeState,
   stopTasks,
   resumeTasks,
@@ -94,7 +95,10 @@ import {
   requestUpdateUser,
   getTaskProgress,
   getSegmentReloadStatus,
-  getTaskRuntimeConfig
+  getTaskRuntimeConfig,
+  getSchemaInfo,
+  getSegmentsStatus,
+  getServerToSegmentsCount
 } from '../requests';
 import { baseApi } from './axios-config';
 import Utils, { getDisplaySegmentStatus } from './Utils';
@@ -380,17 +384,13 @@ const allSchemaDetailsColumnHeader = ["Schema Name", 
"Dimension Columns", "Date-
 
 const getAllSchemaDetails = async (schemaList) => {
   let schemaDetails:Array<any> = [];
-  let promiseArr = [];
-  promiseArr = schemaList.map(async (o)=>{
-    return await getSchema(o);
-  });
-  const results = await Promise.all(promiseArr);
+  const results = await getSchemaDataInfo();
   schemaDetails = results.map((obj)=>{
     let schemaObj = [];
-    schemaObj.push(obj.data.schemaName);
-    schemaObj.push(obj.data.dimensionFieldSpecs ? 
obj.data.dimensionFieldSpecs.length : 0);
-    schemaObj.push(obj.data.dateTimeFieldSpecs ? 
obj.data.dateTimeFieldSpecs.length : 0);
-    schemaObj.push(obj.data.metricFieldSpecs ? 
obj.data.metricFieldSpecs.length : 0);
+    schemaObj.push(obj.schemaName);
+    schemaObj.push(obj.numDimensionFields);
+    schemaObj.push(obj.numDateTimeFields);
+    schemaObj.push(obj.numMetricFields);
     schemaObj.push(schemaObj[1] + schemaObj[2] + schemaObj[3]);
     return schemaObj;
   })
@@ -506,28 +506,38 @@ const getTableSummaryData = (tableName) => {
   });
 };
 
-// This method is used to display segment list of a particular tenant table
-// API: /tables/:tableName/idealstate
-//      /tables/:tableName/externalview
-// Expected Output: {columns: [], records: [], externalViewObject: {}}
-const getSegmentList = (tableName) => {
-  const promiseArr = [];
-  promiseArr.push(getIdealState(tableName));
-  promiseArr.push(getExternalView(tableName));
+// This method is used to display segment list of a particular table with 
segment name and it's status
+// API: /tables/${name}/segmentsStatus
+// Expected Output: {columns: [], records: []}
+  const getSegmentList = (tableName) => {
+    return getSegmentsStatus(tableName).then((results) => {
+      const segmentsArray = results.data; // assuming the array is inside 
`segments` property
+      return {
+        columns: ['Segment Name', 'Status'],
+        records: segmentsArray.map((segment) => [
+          segment.segmentName,
+          segment.segmentStatus
+        ])
+      };
+    });
+  };
 
-  return Promise.all(promiseArr).then((results) => {
-    const idealStateObj = results[0].data.OFFLINE || results[0].data.REALTIME;
-    const externalViewObj = results[1].data.OFFLINE || 
results[1].data.REALTIME;
+const getExternalViewObj = (tableName) => {
+  return getExternalView(tableName).then((result) => {
+    return result.data.OFFLINE || result.data.REALTIME;
+  });
+}; 
 
+const fetchServerToSegmentsCountData = (tableName, tableType) => {
+  return getServerToSegmentsCount(tableName, tableType).then((results) => {
+    const segmentsArray = results.data; 
     return {
-      columns: ['Segment Name', 'Status'],
-      records: Object.keys(idealStateObj).map((key) => {
-        return [
-          key,
-          getDisplaySegmentStatus(idealStateObj[key], externalViewObj[key])
-        ];
-      }),
-      externalViewObj
+      records: segmentsArray.flatMap((server) =>
+        Object.entries(server.serverToSegmentsCountMap).map(([serverName, 
segmentsCount]) => [ 
+          serverName,       
+          segmentsCount    
+        ])
+      )
     };
   });
 };
@@ -806,7 +816,7 @@ const getAllTaskTypes = async () => {
 };
 
 const getTaskInfo = async (taskType) => {
-  const tasksRes = await getTaskTypeTasks(taskType);
+  const tasksResLength = await getTaskTypeTasksCount(taskType);
   const stateRes = await getTaskTypeState(taskType);
 
   let state = get(stateRes, 'data', '');
@@ -814,7 +824,7 @@ const getTaskInfo = async (taskType) => {
   if(typeof state !== "string") {
     state = "";
   }
-  return [tasksRes?.data?.length || 0, state];
+  return [tasksResLength.data || 0, state];
 };
 
 const stopAllTasks = (taskType) => {
@@ -1007,6 +1017,12 @@ const getSchemaData = (schemaName) => {
   });
 };
 
+const getSchemaDataInfo = () => {
+  return getSchemaInfo().then((response)=>{
+    return response.data.schemasInfo;
+  });
+};
+
 const getTableState = (tableName, tableType) => {
   return getState(tableName, tableType).then((response)=>{
     return response.data;
@@ -1246,6 +1262,7 @@ export default {
   getAllTableDetails,
   getTableSummaryData,
   getSegmentList,
+  getExternalViewObj,
   getSegmentStatus,
   getTableDetails,
   getSegmentDetails,
@@ -1318,5 +1335,6 @@ export default {
   deleteUser,
   updateUser,
   getAuthUserNameFromAccessToken,
-  getAuthUserEmailFromAccessToken
+  getAuthUserEmailFromAccessToken,
+  fetchServerToSegmentsCountData
 };
diff --git 
a/pinot-controller/src/test/java/org/apache/pinot/controller/helix/SegmentStatusCheckerTest.java
 
b/pinot-controller/src/test/java/org/apache/pinot/controller/helix/SegmentStatusCheckerTest.java
index 81a0f345c1..1edd2176e6 100644
--- 
a/pinot-controller/src/test/java/org/apache/pinot/controller/helix/SegmentStatusCheckerTest.java
+++ 
b/pinot-controller/src/test/java/org/apache/pinot/controller/helix/SegmentStatusCheckerTest.java
@@ -18,8 +18,13 @@
  */
 package org.apache.pinot.controller.helix;
 
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeMap;
 import org.apache.helix.AccessOption;
 import org.apache.helix.model.ExternalView;
 import org.apache.helix.model.IdealState;
@@ -36,6 +41,8 @@ import org.apache.pinot.common.metrics.MetricValueUtils;
 import org.apache.pinot.common.utils.LLCSegmentName;
 import org.apache.pinot.controller.ControllerConf;
 import org.apache.pinot.controller.LeadControllerManager;
+import org.apache.pinot.controller.api.resources.SegmentStatusInfo;
+import org.apache.pinot.controller.api.resources.TableViews;
 import org.apache.pinot.controller.helix.core.PinotHelixResourceManager;
 import org.apache.pinot.controller.util.TableSizeReader;
 import org.apache.pinot.spi.config.table.TableConfig;
@@ -43,6 +50,7 @@ import org.apache.pinot.spi.config.table.TableType;
 import org.apache.pinot.spi.metrics.PinotMetricUtils;
 import org.apache.pinot.spi.utils.CommonConstants;
 import org.apache.pinot.spi.utils.CommonConstants.Segment.Realtime.Status;
+import org.apache.pinot.spi.utils.JsonUtils;
 import org.apache.pinot.spi.utils.builder.TableConfigBuilder;
 import org.apache.pinot.spi.utils.builder.TableNameBuilder;
 import org.testng.annotations.Test;
@@ -519,4 +527,177 @@ public class SegmentStatusCheckerTest {
     runSegmentStatusChecker(resourceManager, 0);
     verifyControllerMetrics(OFFLINE_TABLE_NAME, 1, numSegments, numSegments, 
0, 0, 0, 99, 0, 246800);
   }
+
+  @Test
+  public void testAllSegmentsGoodOnlineOfflineTable() {
+    TableViews.TableView tableViewExternal = new TableViews.TableView();
+    TableViews.TableView tableViewIdeal = new TableViews.TableView();
+    Map<String, Map<String, String>> tableViewExternalOffline = new 
TreeMap<>();
+    Map<String, Map<String, String>> tableViewIdealOffline = new TreeMap<>();
+    Map<String, String> testSegment1MapExternal = new LinkedHashMap<>();
+    testSegment1MapExternal.put("Server1", "ONLINE");
+    tableViewExternalOffline.put("TestSegment1", testSegment1MapExternal);
+    tableViewExternalOffline.put("TestSegment2", testSegment1MapExternal);
+    Map<String, String> testSegment1MapIdeal = new LinkedHashMap<>();
+    testSegment1MapIdeal.put("Server1", "ONLINE");
+    tableViewIdealOffline.put("TestSegment1", testSegment1MapIdeal);
+    tableViewIdealOffline.put("TestSegment2", testSegment1MapIdeal);
+    tableViewExternal._offline = tableViewExternalOffline;
+    tableViewIdeal._offline = tableViewIdealOffline;
+    TableViews tableviews = new TableViews();
+    List<SegmentStatusInfo> segmentStatusInfos = 
tableviews.getSegmentStatuses(tableViewExternal, tableViewIdeal);
+    assertEquals(segmentStatusInfos.get(0).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.GOOD);
+    assertEquals(segmentStatusInfos.get(1).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.GOOD);
+  }
+
+  @Test
+  public void testAllSegmentsGoodConsumingOfflineTable() {
+    TableViews.TableView tableViewExternal = new TableViews.TableView();
+    TableViews.TableView tableViewIdeal = new TableViews.TableView();
+    Map<String, Map<String, String>> tableViewExternalOffline = new 
TreeMap<>();
+    Map<String, Map<String, String>> tableViewIdealOffline = new TreeMap<>();
+    Map<String, String> testSegment1MapExternal = new LinkedHashMap<>();
+    testSegment1MapExternal.put("Server1", "CONSUMING");
+    tableViewExternalOffline.put("TestSegment1", testSegment1MapExternal);
+    tableViewExternalOffline.put("TestSegment2", testSegment1MapExternal);
+    Map<String, String> testSegment1MapIdeal = new LinkedHashMap<>();
+    testSegment1MapIdeal.put("Server1", "CONSUMING");
+    tableViewIdealOffline.put("TestSegment1", testSegment1MapIdeal);
+    tableViewIdealOffline.put("TestSegment2", testSegment1MapIdeal);
+    tableViewExternal._offline = tableViewExternalOffline;
+    tableViewIdeal._offline = tableViewIdealOffline;
+    TableViews tableviews = new TableViews();
+    List<SegmentStatusInfo> segmentStatusInfos = 
tableviews.getSegmentStatuses(tableViewExternal, tableViewIdeal);
+    assertEquals(segmentStatusInfos.get(0).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.GOOD);
+    assertEquals(segmentStatusInfos.get(1).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.GOOD);
+  }
+
+  @Test
+  public void testAllSegmentsBadOfflineTable() {
+    TableViews.TableView tableViewExternal = new TableViews.TableView();
+    TableViews.TableView tableViewIdeal = new TableViews.TableView();
+    Map<String, Map<String, String>> tableViewExternalOffline = new 
TreeMap<>();
+    Map<String, Map<String, String>> tableViewIdealOffline = new TreeMap<>();
+    Map<String, String> testSegment1MapExternal = new LinkedHashMap<>();
+    testSegment1MapExternal.put("Server1", "ERROR");
+    tableViewExternalOffline.put("TestSegment1", testSegment1MapExternal);
+    tableViewExternalOffline.put("TestSegment2", testSegment1MapExternal);
+    Map<String, String> testSegment1MapIdeal = new LinkedHashMap<>();
+    testSegment1MapIdeal.put("Server1", "ONLINE");
+    tableViewIdealOffline.put("TestSegment1", testSegment1MapIdeal);
+    tableViewIdealOffline.put("TestSegment2", testSegment1MapIdeal);
+    tableViewExternal._offline = tableViewExternalOffline;
+    tableViewIdeal._offline = tableViewIdealOffline;
+    TableViews tableviews = new TableViews();
+    List<SegmentStatusInfo> segmentStatusInfos = 
tableviews.getSegmentStatuses(tableViewExternal, tableViewIdeal);
+    assertEquals(segmentStatusInfos.get(0).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.BAD);
+    assertEquals(segmentStatusInfos.get(1).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.BAD);
+  }
+
+  @Test
+  public void testAllSegmentsUpdatingOfflineTable() {
+    TableViews.TableView tableViewExternal = new TableViews.TableView();
+    TableViews.TableView tableViewIdeal = new TableViews.TableView();
+    Map<String, Map<String, String>> tableViewExternalOffline = new 
TreeMap<>();
+    Map<String, Map<String, String>> tableViewIdealOffline = new TreeMap<>();
+    Map<String, String> testSegment1MapExternal = new LinkedHashMap<>();
+    testSegment1MapExternal.put("Server1", "OFFLINE");
+    tableViewExternalOffline.put("TestSegment1", testSegment1MapExternal);
+    tableViewExternalOffline.put("TestSegment2", testSegment1MapExternal);
+    Map<String, String> testSegment1MapIdeal = new LinkedHashMap<>();
+    testSegment1MapIdeal.put("Server1", "ONLINE");
+    tableViewIdealOffline.put("TestSegment1", testSegment1MapIdeal);
+    tableViewIdealOffline.put("TestSegment2", testSegment1MapIdeal);
+    tableViewExternal._offline = tableViewExternalOffline;
+    tableViewIdeal._offline = tableViewIdealOffline;
+    TableViews tableviews = new TableViews();
+    List<SegmentStatusInfo> segmentStatusInfos = 
tableviews.getSegmentStatuses(tableViewExternal, tableViewIdeal);
+    assertEquals(segmentStatusInfos.get(0).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.UPDATING);
+    assertEquals(segmentStatusInfos.get(1).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.UPDATING);
+  }
+
+  @Test
+  public void testAllSegmentsGoodBadOfflineTable() {
+    TableViews.TableView tableViewExternal = new TableViews.TableView();
+    TableViews.TableView tableViewIdeal = new TableViews.TableView();
+    Map<String, Map<String, String>> tableViewExternalOffline = new 
TreeMap<>();
+    Map<String, Map<String, String>> tableViewIdealOffline = new TreeMap<>();
+    Map<String, String> testSegment1MapExternal = new LinkedHashMap<>();
+    Map<String, String> testSegment2MapExternal = new LinkedHashMap<>();
+    testSegment1MapExternal.put("Server1", "OFFLINE");
+    testSegment2MapExternal.put("Server2", "ERROR");
+    tableViewExternalOffline.put("TestSegment1", testSegment1MapExternal);
+    tableViewExternalOffline.put("TestSegment2", testSegment2MapExternal);
+    Map<String, String> testSegment1MapIdeal = new LinkedHashMap<>();
+    testSegment1MapIdeal.put("Server1", "OFFLINE");
+    Map<String, String> testSegment2MapIdeal = new LinkedHashMap<>();
+    testSegment2MapIdeal.put("Server2", "ERROR");
+    tableViewIdealOffline.put("TestSegment1", testSegment1MapIdeal);
+    tableViewIdealOffline.put("TestSegment2", testSegment2MapIdeal);
+    tableViewExternal._offline = tableViewExternalOffline;
+    tableViewIdeal._offline = tableViewIdealOffline;
+    TableViews tableviews = new TableViews();
+    List<SegmentStatusInfo> segmentStatusInfos = 
tableviews.getSegmentStatuses(tableViewExternal, tableViewIdeal);
+    assertEquals(segmentStatusInfos.get(0).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.GOOD);
+    assertEquals(segmentStatusInfos.get(1).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.BAD);
+  }
+
+  @Test
+  public void testJsonDeserializationSegmentStatusInfo()
+      throws Exception {
+    // JSON string representing SchemaInfo
+    String json = "[\n" + "  {\n" + "    \"segmentStatus\": \"GOOD\",\n"
+        + "    \"segmentName\": \"airlineStats_OFFLINE_16071_16071_0\"\n" + "  
},\n" + "  {\n"
+        + "    \"segmentStatus\": \"BAD\",\n" + "    \"segmentName\": 
\"airlineStats_OFFLINE_16072_16072_0\"\n"
+        + "  },\n" + "  {\n" + "    \"segmentStatus\": \"UPDATING\",\n"
+        + "    \"segmentName\": \"airlineStats_OFFLINE_16073_16073_0\"\n" + "  
}\n" + "]";
+    JsonNode jsonNode = JsonUtils.stringToJsonNode(json);
+    List<SegmentStatusInfo> segmentStatusInfos =
+        JsonUtils.jsonNodeToObject(jsonNode, new 
TypeReference<List<SegmentStatusInfo>>() {
+        });
+    // Assertions
+    assertEquals(segmentStatusInfos.size(), 3);
+    assertEquals(segmentStatusInfos.get(0).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.GOOD);
+    assertEquals(segmentStatusInfos.get(0).getSegmentName(), 
"airlineStats_OFFLINE_16071_16071_0");
+    assertEquals(segmentStatusInfos.get(1).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.BAD);
+    assertEquals(segmentStatusInfos.get(1).getSegmentName(), 
"airlineStats_OFFLINE_16072_16072_0");
+    assertEquals(segmentStatusInfos.get(2).getSegmentStatus(),
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.UPDATING);
+    assertEquals(segmentStatusInfos.get(2).getSegmentName(), 
"airlineStats_OFFLINE_16073_16073_0");
+  }
+
+  @Test
+  public void testJsonSerializationSegmentStatusInfo()
+      throws Exception {
+    SegmentStatusInfo statusInfo1 = new 
SegmentStatusInfo("airlineStats_OFFLINE_16071_16071_0",
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.GOOD);
+    SegmentStatusInfo statusInfo2 = new 
SegmentStatusInfo("airlineStats_OFFLINE_16072_16072_0",
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.BAD);
+    SegmentStatusInfo statusInfo3 = new 
SegmentStatusInfo("airlineStats_OFFLINE_16073_16073_0",
+        CommonConstants.Helix.StateModel.DisplaySegmentStatus.UPDATING);
+    List<SegmentStatusInfo> segmentStatusInfoList = new ArrayList<>();
+    segmentStatusInfoList.add(statusInfo1);
+    segmentStatusInfoList.add(statusInfo2);
+    segmentStatusInfoList.add(statusInfo3);
+    String json =
+        "[ {\n" + "  \"segmentName\" : 
\"airlineStats_OFFLINE_16071_16071_0\",\n" + "  \"segmentStatus\" : \"GOOD\"\n"
+            + "}, {\n" + "  \"segmentName\" : 
\"airlineStats_OFFLINE_16072_16072_0\",\n"
+            + "  \"segmentStatus\" : \"BAD\"\n" + "}, {\n"
+            + "  \"segmentName\" : \"airlineStats_OFFLINE_16073_16073_0\",\n" 
+ "  \"segmentStatus\" : \"UPDATING\"\n"
+            + "} ]";
+    String jsonString = JsonUtils.objectToPrettyString(segmentStatusInfoList);
+    assertEquals(jsonString, json);
+  }
 }
diff --git a/pinot-core/src/main/java/org/apache/pinot/core/auth/Actions.java 
b/pinot-core/src/main/java/org/apache/pinot/core/auth/Actions.java
index 62f1b2e4d6..9d0695a285 100644
--- a/pinot-core/src/main/java/org/apache/pinot/core/auth/Actions.java
+++ b/pinot-core/src/main/java/org/apache/pinot/core/auth/Actions.java
@@ -58,6 +58,7 @@ public class Actions {
     public static final String GET_RUNNING_QUERY = "GetRunningQuery";
     public static final String GET_SCHEDULER_INFO = "GetSchedulerInfo";
     public static final String GET_SCHEMA = "GetSchema";
+    public static final String GET_SCHEMAS_INFO = "GetSchemasInfo";
     public static final String GET_SEGMENT = "GetSegment";
     public static final String GET_SEGMENT_RELOAD_STATUS = 
"GetSegmentReloadStatus";
     public static final String GET_SERVER_ROUTING_STATS = 
"GetServerRoutingStats";
@@ -66,6 +67,7 @@ public class Actions {
     public static final String GET_TABLE_CONFIG = "GetTableConfig";
     public static final String GET_TABLE_LEADER = "GetTableLeader";
     public static final String GET_TASK = "GetTask";
+    public static final String GET_TASK_COUNT = "GetTaskCount";
     public static final String GET_TENANT = "GetTenant";
     public static final String GET_USER = "GetUser";
     public static final String GET_VERSION = "GetVersion";
@@ -116,6 +118,7 @@ public class Actions {
     public static final String GET_CONTROLLER_JOBS = "GetControllerJobs";
     public static final String GET_DEBUG_INFO = "GetDebugInfo";
     public static final String GET_EXTERNAL_VIEW = "GetExternalView";
+    public static final String GET_SEGMENT_STATUS = "GetSegmentStatus";
     public static final String GET_IDEAL_STATE = "GetIdealState";
     public static final String GET_INSTANCE = "GetInstance";
     public static final String GET_INSTANCE_PARTITIONS = 
"GetInstancePartitions";
diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/data/SchemaInfo.java 
b/pinot-spi/src/main/java/org/apache/pinot/spi/data/SchemaInfo.java
new file mode 100644
index 0000000000..5949fcdcad
--- /dev/null
+++ b/pinot-spi/src/main/java/org/apache/pinot/spi/data/SchemaInfo.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.spi.data;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+
+/**
+ * This class gives the details of a particular schema and the corresponding 
column count metrics
+ *
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class SchemaInfo {
+  @JsonProperty("schemaName")
+  private String _schemaName;
+
+  @JsonProperty("numDimensionFields")
+  private int _numDimensionFields;
+
+  @JsonProperty("numDateTimeFields")
+  private int _numDateTimeFields;
+
+  @JsonProperty("numMetricFields")
+  private int _numMetricFields;
+
+  public String getSchemaName() {
+    return _schemaName;
+  }
+
+  public int getNumDimensionFields() {
+    return _numDimensionFields;
+  }
+
+  public int getNumDateTimeFields() {
+    return _numDateTimeFields;
+  }
+
+  public int getNumMetricFields() {
+    return _numMetricFields;
+  }
+
+  public SchemaInfo() {
+  }
+
+  public SchemaInfo(Schema schema) {
+    _schemaName = schema.getSchemaName();
+
+    //Removed virtual columns($docId, $hostName, $segmentName) from dimension 
fields count
+    _numDimensionFields = schema.getDimensionFieldSpecs().size() - 3;
+    _numDateTimeFields = schema.getDateTimeFieldSpecs().size();
+    _numMetricFields = schema.getMetricFieldSpecs().size();
+  }
+}
diff --git 
a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/CommonConstants.java 
b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/CommonConstants.java
index 21e5c87ee7..c1c4dbce49 100644
--- a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/CommonConstants.java
+++ b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/CommonConstants.java
@@ -139,6 +139,12 @@ public class CommonConstants {
         public static final String CONSUMING = "CONSUMING";
       }
 
+      public static class DisplaySegmentStatus {
+        public static final String BAD = "BAD";
+        public static final String GOOD = "GOOD";
+        public static final String UPDATING = "UPDATING";
+      }
+
       public static class BrokerResourceStateModel {
         public static final String ONLINE = "ONLINE";
         public static final String OFFLINE = "OFFLINE";
diff --git 
a/pinot-spi/src/test/java/org/apache/pinot/spi/data/SchemaInfoTest.java 
b/pinot-spi/src/test/java/org/apache/pinot/spi/data/SchemaInfoTest.java
new file mode 100644
index 0000000000..5f31e19ed8
--- /dev/null
+++ b/pinot-spi/src/test/java/org/apache/pinot/spi/data/SchemaInfoTest.java
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.spi.data;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.pinot.spi.utils.CommonConstants;
+import org.apache.pinot.spi.utils.JsonUtils;
+import org.testng.annotations.Test;
+
+import static org.apache.pinot.spi.data.FieldSpec.DataType.INT;
+import static org.testng.Assert.assertEquals;
+
+
+public class SchemaInfoTest {
+
+  @Test
+  public void testSchemaInfoSerDeserWithVirtualColumns()
+      throws IOException {
+    // Mock the Schema objects
+    Schema schemaMock =
+        new 
Schema.SchemaBuilder().setSchemaName("TestSchema").addDimensionField("dim1", 
FieldSpec.DataType.STRING)
+            .addDimensionField("dim2", 
FieldSpec.DataType.INT).addDimensionField("dim3", FieldSpec.DataType.INT)
+            
.addDimensionField(CommonConstants.Segment.BuiltInVirtualColumn.DOCID, 
FieldSpec.DataType.INT)
+            
.addDimensionField(CommonConstants.Segment.BuiltInVirtualColumn.HOSTNAME, 
FieldSpec.DataType.STRING)
+            
.addDimensionField(CommonConstants.Segment.BuiltInVirtualColumn.SEGMENTNAME, 
FieldSpec.DataType.STRING)
+            .addDateTimeField("dt1", FieldSpec.DataType.LONG, "1:HOURS:EPOCH", 
"1:HOURS")
+            .addDateTimeField("dt2", FieldSpec.DataType.LONG, "1:HOURS:EPOCH", 
"1:HOURS").addMetricField("metric", INT)
+            .build();
+    SchemaInfo schemaInfo = new SchemaInfo(schemaMock);
+    List<SchemaInfo> schemaInfoList = new ArrayList<>();
+    schemaInfoList.add(schemaInfo);
+    String response = JsonUtils.objectToPrettyString(schemaInfoList);
+    JsonNode jsonNodeResp = JsonUtils.stringToJsonNode(response);
+
+    // Test deserialization
+    assertEquals(jsonNodeResp.get(0).get("schemaName").asText(), "TestSchema");
+    assertEquals(jsonNodeResp.get(0).get("numDimensionFields").asInt(), 3);
+    assertEquals(jsonNodeResp.get(0).get("numDateTimeFields").asInt(), 2);
+    assertEquals(jsonNodeResp.get(0).get("numMetricFields").asInt(), 1);
+    assertEquals(schemaInfo.getSchemaName(), "TestSchema");
+
+    // Test column count
+    assertEquals(schemaInfo.getNumDimensionFields(), 3);  // 6 - 3 virtual 
columns = 3
+    assertEquals(schemaInfo.getNumDateTimeFields(), 2);
+    assertEquals(schemaInfo.getNumMetricFields(), 1);
+
+    // Serialize JsonNode back to SchemaInfo
+    List<SchemaInfo> schemaInfoListSer = new ArrayList<>();
+    schemaInfoListSer = JsonUtils.jsonNodeToObject(jsonNodeResp, new 
TypeReference<List<SchemaInfo>>() {
+    });
+    SchemaInfo schemaInfo1 = schemaInfoListSer.get(0);
+    // Verify the deserialized object match
+    assertEquals(schemaInfo1.getSchemaName(), "TestSchema");
+    assertEquals(schemaInfo1.getNumDimensionFields(), 3);
+    assertEquals(schemaInfo1.getNumDateTimeFields(), 2);
+    assertEquals(schemaInfo1.getNumMetricFields(), 1);
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org
For additional commands, e-mail: commits-h...@pinot.apache.org

Reply via email to