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]