This is an automated email from the ASF dual-hosted git repository.
tkhurana pushed a commit to branch PHOENIX-7562-feature-new
in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/PHOENIX-7562-feature-new by
this push:
new 69378c6ce3 Handling Unknown role results from Server (#2360)
69378c6ce3 is described below
commit 69378c6ce3dc21523af90ad664df235b715aa813
Author: Lokesh Khurana <[email protected]>
AuthorDate: Mon Feb 9 10:39:29 2026 -0800
Handling Unknown role results from Server (#2360)
---
.../org/apache/phoenix/jdbc/ClusterRoleRecord.java | 5 +++
.../apache/phoenix/jdbc/HighAvailabilityGroup.java | 49 ++++++++++++++++++++--
.../phoenix/util/GetClusterRoleRecordUtil.java | 1 -
.../phoenix/jdbc/HighAvailabilityGroup2IT.java | 27 ++++++++++++
4 files changed, 77 insertions(+), 5 deletions(-)
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/ClusterRoleRecord.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/ClusterRoleRecord.java
index 7e2e9c2639..3f5375db9e 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/ClusterRoleRecord.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/ClusterRoleRecord.java
@@ -221,6 +221,11 @@ public class ClusterRoleRecord {
return haGroupName.equals(other.haGroupName) &&
policy.equals(other.policy);
}
+ /** Returns true if CRR has any url in UNKNOWN role/state. */
+ public boolean hasUnknownRole() {
+ return role1 == ClusterRole.UNKNOWN || role2 == ClusterRole.UNKNOWN;
+ }
+
/** Returns role by url or UNKNOWN if the Url does not belong to this HA
group */
public ClusterRole getRole(String url) {
if (url1.equals(url)) {
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HighAvailabilityGroup.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HighAvailabilityGroup.java
index 3b8d63c787..87a6700eca 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HighAvailabilityGroup.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HighAvailabilityGroup.java
@@ -954,12 +954,53 @@ public class HighAvailabilityGroup {
Long.parseLong(properties.getProperty(PHOENIX_HA_CRR_POLLER_INTERVAL_MS_KEY,
config
.get(PHOENIX_HA_CRR_POLLER_INTERVAL_MS_KEY,
PHOENIX_HA_CRR_POLLER_INTERVAL_MS_DEFAULT)));
- // Get the CRR via RSEndpoint for cluster 1
try {
- return GetClusterRoleRecordUtil.fetchClusterRoleRecord(info.getUrl1(),
info.getName(), this,
- pollerInterval, properties);
+ // Get the CRR via RSEndpoint for cluster 1
+ ClusterRoleRecord roleRecord =
GetClusterRoleRecordUtil.fetchClusterRoleRecord(info.getUrl1(),
+ info.getName(), this, pollerInterval, properties);
+ // If we have unknown role for any cluster then try getting CRR from
cluster 2 endpoint and if
+ // we get unknown role from there as well then CRR with higher
adminVersion wins.
+ if (roleRecord.hasUnknownRole()) {
+ ClusterRoleRecord roleRecordFromPR;
+ try {
+ roleRecordFromPR =
GetClusterRoleRecordUtil.fetchClusterRoleRecord(info.getUrl2(),
+ info.getName(), this, pollerInterval, properties);
+ } catch (Exception e) {
+ // As we were able to get CRR from cluster 1 but cluster 2 threw
exception then just
+ // return
+ // CRR from cluster 1 and consume this exception
+ LOG.warn("Role Record from cluster {} has Unknown Role but cluster
{} threw exception, "
+ + "returning {} as CRR", info.getUrl1(), info.getUrl2(),
roleRecord.toPrettyString());
+ return roleRecord;
+ }
+ if (roleRecordFromPR.hasUnknownRole()) {
+ return roleRecord.getVersion() > roleRecordFromPR.getVersion()
+ ? roleRecord
+ : roleRecordFromPR;
+ } else {
+ return roleRecordFromPR;
+ }
+ } else {
+ return roleRecord;
+ }
} catch (Exception e) {
- // Got exception from cluster 1 when trying to get CRR, try cluster 2
+ // If we get CRR Not Found on cluster 1, we should still try cluster 2,
maybe
+ // haGroupStoreClient
+ // was not initialized somehow, but if we get any exception from cluster
2 too then we should
+ // throw CRR not found so that fallback can happen.
+ if (
+ e instanceof SQLException && ((SQLException) e).getErrorCode()
+ == SQLExceptionCode.CLUSTER_ROLE_RECORD_NOT_FOUND.getErrorCode()
+ ) {
+ try {
+ return
GetClusterRoleRecordUtil.fetchClusterRoleRecord(info.getUrl2(), info.getName(),
+ this, pollerInterval, properties);
+ } catch (Exception ignoredEx) {
+ throw (SQLException) e;
+ }
+ }
+
+ // If caught exception is not CRR not found, then just try cluster 2
endpoint.
return GetClusterRoleRecordUtil.fetchClusterRoleRecord(info.getUrl2(),
info.getName(), this,
pollerInterval, properties);
}
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/util/GetClusterRoleRecordUtil.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/util/GetClusterRoleRecordUtil.java
index 4a2073ff2b..4e43c72feb 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/util/GetClusterRoleRecordUtil.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/util/GetClusterRoleRecordUtil.java
@@ -142,7 +142,6 @@ public class GetClusterRoleRecordUtil {
public static ClusterRoleRecord fetchClusterRoleRecord(String url, String
haGroupName,
HighAvailabilityGroup haGroup, long pollerInterval, Properties properties)
throws SQLException {
ClusterRoleRecord clusterRoleRecord = getClusterRoleRecord(url,
haGroupName, true, properties);
- // TODO: Will need to handle UNKNOWN role cases...
if (
clusterRoleRecord.getPolicy() == HighAvailabilityPolicy.FAILOVER
&& !clusterRoleRecord.getRole1().isActive() &&
!clusterRoleRecord.getRole2().isActive()
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HighAvailabilityGroup2IT.java
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HighAvailabilityGroup2IT.java
index 23e6fab17a..9dddfe4b8a 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HighAvailabilityGroup2IT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HighAvailabilityGroup2IT.java
@@ -323,6 +323,33 @@ public class HighAvailabilityGroup2IT extends HABaseIT {
}
}
+ /**
+ * Test Fallback to Single Cluster if non Key cluster is down
+ */
+ @Test
+ public void testFallbackToSingleClusterIfNonKeyClusterIsDown() throws
Exception {
+ final String tableName = RandomStringUtils.randomAlphabetic(10);
+ CLUSTERS.createTableOnClusterPair(haGroup, tableName);
+ String haGroupName2 = testName.getMethodName() +
RandomStringUtils.randomAlphabetic(3);
+
+ String firstClusterUrl = CLUSTERS.getJdbcUrl1(haGroup);
+ clientProperties.setProperty(PHOENIX_HA_GROUP_ATTR, haGroupName2);
+ clientProperties.setProperty(PHOENIX_HA_FALLBACK_CLUSTER_KEY,
firstClusterUrl);
+
+ // Here cluster 2 is down and fallback key is cluster 1
+ CLUSTERS.doTestWhenOneZKDown(CLUSTERS.getHBaseCluster2(), () -> {
+ try {
+ // Should get Fallback connection as fallback key is cluster 1 and
cluster 2 is down
+ Connection conn = DriverManager.getConnection(jdbcHAUrl,
clientProperties);
+ assertTrue(conn instanceof PhoenixConnection);
+ assertEquals(firstClusterUrl, ((PhoenixConnection) conn).getURL());
+ doTestBasicOperationsWithConnection(conn, tableName, haGroupName2);
+ } catch (SQLException e) {
+ fail("Should have failed since one HA group can not initialized. Not
falling back");
+ }
+ });
+ }
+
/**
* Test that poller should be running when we detect a non-active role
record and should stop when
* we detect an active role record