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

xiangfu 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 605cd8a1a97 The Pinot UI should distinguish between a server being up 
and being healthy #16775 (#17248)
605cd8a1a97 is described below

commit 605cd8a1a9755050cccc58c21f67bd654df47b6a
Author: Akanksha kedia <[email protected]>
AuthorDate: Fri Jan 2 13:26:47 2026 +0530

    The Pinot UI should distinguish between a server being up and being healthy 
#16775 (#17248)
---
 .../resources/PinotInstanceRestletResource.java    |  4 ++
 .../src/main/resources/app/interfaces/types.d.ts   |  1 +
 .../main/resources/app/utils/PinotMethodUtils.ts   | 83 +++++++++++++++++++---
 3 files changed, 78 insertions(+), 10 deletions(-)

diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotInstanceRestletResource.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotInstanceRestletResource.java
index d4090a84087..cdd8bfdad7c 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotInstanceRestletResource.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotInstanceRestletResource.java
@@ -159,6 +159,10 @@ public class PinotInstanceRestletResource {
     if ("true".equalsIgnoreCase(queriesDisabled)) {
       response.put(CommonConstants.Helix.QUERIES_DISABLED, "true");
     }
+    // Add shutdown in progress status
+    boolean shutdownInProgress = instanceConfig.getRecord().getBooleanField(
+        CommonConstants.Helix.IS_SHUTDOWN_IN_PROGRESS, false);
+    response.put(CommonConstants.Helix.IS_SHUTDOWN_IN_PROGRESS, 
shutdownInProgress);
     response.put("systemResourceInfo", 
JsonUtils.objectToJsonNode(getSystemResourceInfo(instanceConfig)));
     return response.toString();
   }
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 3f56c3832f5..634dccd5789 100644
--- a/pinot-controller/src/main/resources/app/interfaces/types.d.ts
+++ b/pinot-controller/src/main/resources/app/interfaces/types.d.ts
@@ -72,6 +72,7 @@ declare module 'Models' {
     queryServicePort: number;
     queryMailboxPort: number;
     queriesDisabled: boolean;
+    shutdownInProgress?: boolean;
     tags: Array<string>;
     pools?: string;
   };
diff --git a/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts 
b/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts
index 584e92cb6ef..cb6d6643ce9 100644
--- a/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts
+++ b/pinot-controller/src/main/resources/app/utils/PinotMethodUtils.ts
@@ -184,24 +184,87 @@ const getAllInstances = () => {
   });
 };
 
+// This method is used to check the health endpoint of an instance
+// API: http://{hostname}:{port}/health
+// Expected Output: 'OK' or error
+const checkInstanceHealth = async (hostname, port) => {
+  try {
+    const response = await baseApi.get(`http://${hostname}:${port}/health`, {
+      timeout: 5000 // 5 second timeout
+    });
+    return response.data === 'OK' || response.status === 200;
+  } catch (error) {
+    return false;
+  }
+};
+
 // This method is used to display instance data on cluster manager home page
 // API: /instances/:instanceName
 // Expected Output: {columns: [], records: []}
 const getInstanceData = (instances, liveInstanceArr) => {
   const promiseArr = [...instances.map((inst) => getInstance(inst))];
 
-  return Promise.all(promiseArr).then((result) => {
+  return Promise.all(promiseArr).then(async (result) => {
+    // First, get all instance configs
+    const instanceRecords = result.map(({ data }) => {
+      const isAlive = liveInstanceArr.indexOf(data.instanceName) > -1;
+      const isEnabled = data.enabled;
+      const queriesDisabled = data.queriesDisabled === 'true' || 
data.queriesDisabled === true;
+      const shutdownInProgress = data.shutdownInProgress === true;
+      
+      return {
+        instanceName: data.instanceName,
+        enabled: data.enabled,
+        hostName: data.hostName,
+        port: data.port,
+        adminPort: data.adminPort,
+        isAlive,
+        isEnabled,
+        queriesDisabled,
+        shutdownInProgress
+      };
+    });
+
+    // Then check health endpoints for alive instances
+    const healthCheckPromises = instanceRecords.map(async (record) => {
+      if (!record.isAlive) {
+        return { ...record, healthStatus: 'Dead' };
+      }
+
+      // Determine which port to use for health check
+      // For brokers and servers, use adminPort if available, otherwise use 
main port
+      const healthPort = record.adminPort && record.adminPort > 0 ? 
record.adminPort : record.port;
+      const isHealthy = await checkInstanceHealth(record.hostName, healthPort);
+
+      let status = 'Dead';
+      if (record.isAlive) {
+        if (record.shutdownInProgress) {
+          status = 'Shutting Down';
+        } else if (!record.isEnabled) {
+          status = 'Disabled';
+        } else if (record.queriesDisabled) {
+          status = 'Queries Disabled';
+        } else if (isHealthy) {
+          status = 'Healthy';
+        } else {
+          status = 'Unhealthy';
+        }
+      }
+
+      return { ...record, healthStatus: status };
+    });
+
+    const recordsWithHealth = await Promise.all(healthCheckPromises);
+
     return {
       columns: ['Instance Name', 'Enabled', 'Hostname', 'Port', 'Status'],
-      records: [
-        ...result.map(({ data }) => [
-          data.instanceName,
-          data.enabled,
-          data.hostName,
-          data.port,
-          liveInstanceArr.indexOf(data.instanceName) > -1 ? 'Alive' : 'Dead'
-        ]),
-      ],
+      records: recordsWithHealth.map(record => [
+        record.instanceName,
+        record.enabled,
+        record.hostName,
+        record.port,
+        record.healthStatus
+      ])
     };
   });
 };


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to