This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/helix.git
The following commit(s) were added to refs/heads/master by this push:
new 3332c2223 Support Helix REST API isClusterStable
3332c2223 is described below
commit 3332c222364f3bfdc3964db7d5a4bd1c066a0795
Author: Junkai Xue <[email protected]>
AuthorDate: Mon Jun 29 13:23:05 2026 -0700
Support Helix REST API isClusterStable
---
.../server/resources/helix/ClusterAccessor.java | 34 ++++++++++++++++++++++
.../helix/rest/server/TestClusterAccessor.java | 20 +++++++++++++
2 files changed, 54 insertions(+)
diff --git
a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
index 1e752f3fe..c35db12d6 100644
---
a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
+++
b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
@@ -77,6 +77,7 @@ import org.apache.helix.rest.server.service.ClusterService;
import org.apache.helix.rest.server.service.ClusterServiceImpl;
import org.apache.helix.rest.server.service.VirtualTopologyGroupService;
import org.apache.helix.tools.ClusterSetup;
+import
org.apache.helix.tools.ClusterVerifiers.BestPossibleExternalViewVerifier;
import org.apache.helix.zookeeper.api.client.RealmAwareZkClient;
import org.apache.helix.zookeeper.datamodel.ZNRecord;
import org.apache.zookeeper.data.Stat;
@@ -402,6 +403,39 @@ public class ClusterAccessor extends AbstractHelixResource
{
return JSONRepresentation(responseMap);
}
+ @ClusterAuth
+ @ResponseMetered(name = HttpConstants.READ_REQUEST)
+ @Timed(name = HttpConstants.READ_REQUEST)
+ @GET
+ @Path("{clusterId}/isStable")
+ @ApiOperation(value = "Check whether the cluster is stable",
+ notes = "Returns true if every resource's ExternalView matches its best
possible state, "
+ + "meaning there are no ongoing state transitions. Optional query
param 'timeout' (ms) "
+ + "bounds how long to wait for convergence; defaults to 0 for a
single immediate check.")
+ public Response isClusterStable(@PathParam("clusterId") String clusterId,
+ @DefaultValue("0") @QueryParam("timeout") long timeout) {
+ if (!doesClusterExist(clusterId)) {
+ return notFound(String.format("Cluster %s does not exist", clusterId));
+ }
+
+ boolean stable;
+ // Reuse the shared REST server ZkClient; the verifier must not close it
on our behalf.
+ try (BestPossibleExternalViewVerifier verifier =
+ new BestPossibleExternalViewVerifier.Builder(clusterId)
+ .setZkClient(getRealmAwareZkClient())
+ .build()) {
+ stable = verifier.verify(timeout);
+ } catch (Exception e) {
+ LOG.error("Failed to verify cluster stability for cluster {}",
clusterId, e);
+ return serverError(e);
+ }
+
+ Map<String, Object> responseMap = new HashMap<>();
+ responseMap.put("cluster", clusterId);
+ responseMap.put("stable", stable);
+ return JSONRepresentation(responseMap);
+ }
+
private Map<String, Object> getManagementModeDetails(String clusterId,
ClusterManagementMode mode) {
Map<String, Object> details = new HashMap<>();
diff --git
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
index f9af0a9c2..93f4481fa 100644
---
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
+++
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
@@ -176,6 +176,26 @@ public class TestClusterAccessor extends AbstractTestClass
{
System.out.println("End test :" + TestHelper.getTestMethodName());
}
+ @Test(dependsOnMethods = "testGetClusters")
+ public void testIsClusterStable() throws IOException {
+ System.out.println("Start test :" + TestHelper.getTestMethodName());
+ String cluster = "TestCluster_1";
+
+ // Allow some time for the cluster to converge before asserting stability.
+ String body = get("clusters/" + cluster + "/isStable",
+ ImmutableMap.of("timeout", "10000"),
Response.Status.OK.getStatusCode(), true);
+ JsonNode node = OBJECT_MAPPER.readTree(body);
+ Assert.assertEquals(node.get("cluster").asText(), cluster);
+ Assert.assertTrue(node.get("stable").asBoolean(),
+ "Cluster " + cluster + " should be stable");
+
+ // Non-existent cluster should return 404.
+ get("clusters/nonExistentCluster/isStable", null,
Response.Status.NOT_FOUND.getStatusCode(),
+ false);
+
+ System.out.println("End test :" + TestHelper.getTestMethodName());
+ }
+
@Test(dependsOnMethods = "testGetClusters")
public void testGetClusterTopology() {
System.out.println("Start test :" + TestHelper.getTestMethodName());