From eb84f893c71f97fa7ef2cb81246a2a269e33bb63 Mon Sep 17 00:00:00 2001 From: Ritesh Garg Date: Tue, 9 Jun 2026 23:38:13 -0700 Subject: [PATCH] PHOENIX-7566 HAGroupStore state machine: ANISTS non-blocking + DEGRADED_STANDBY -> STANDBY_TO_ACTIVE - Map ACTIVE_NOT_IN_SYNC_TO_STANDBY to ClusterRole ACTIVE (non-blocking); only ACTIVE_IN_SYNC_TO_STANDBY remains mutation-blocking. - Allow DEGRADED_STANDBY -> STANDBY_TO_ACTIVE so a degraded standby can promote on failover. - Tests: HAGroupStoreRecordTest role/mutation-block and transition assertions; HAGroupStoreManagerIT multi-group blocking uses ACTIVE_IN_SYNC_TO_STANDBY. Co-authored-by: Cursor --- .../apache/phoenix/jdbc/HAGroupStoreRecord.java | 4 ++-- .../apache/phoenix/jdbc/HAGroupStoreManagerIT.java | 7 ++++--- .../phoenix/jdbc/HAGroupStoreRecordTest.java | 14 +++++++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreRecord.java b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreRecord.java index da35da13dd1..b72cc39b1cd 100644 --- a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreRecord.java +++ b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreRecord.java @@ -76,11 +76,11 @@ public ClusterRoleRecord.ClusterRole getClusterRole() { case ABORT_TO_ACTIVE_NOT_IN_SYNC: case ACTIVE_IN_SYNC: case ACTIVE_NOT_IN_SYNC: + case ACTIVE_NOT_IN_SYNC_TO_STANDBY: case ACTIVE_NOT_IN_SYNC_WITH_OFFLINE_PEER: case ACTIVE_WITH_OFFLINE_PEER: return ClusterRoleRecord.ClusterRole.ACTIVE; case ACTIVE_IN_SYNC_TO_STANDBY: - case ACTIVE_NOT_IN_SYNC_TO_STANDBY: return ClusterRoleRecord.ClusterRole.ACTIVE_TO_STANDBY; case ABORT_TO_STANDBY: case DEGRADED_STANDBY: @@ -114,7 +114,7 @@ public ClusterRoleRecord.ClusterRole getClusterRole() { ACTIVE_IN_SYNC_TO_STANDBY.allowedTransitions = ImmutableSet.of(ABORT_TO_ACTIVE_IN_SYNC, STANDBY); STANDBY_TO_ACTIVE.allowedTransitions = ImmutableSet.of(ABORT_TO_STANDBY, ACTIVE_IN_SYNC); - DEGRADED_STANDBY.allowedTransitions = ImmutableSet.of(STANDBY); + DEGRADED_STANDBY.allowedTransitions = ImmutableSet.of(STANDBY, STANDBY_TO_ACTIVE); ACTIVE_WITH_OFFLINE_PEER.allowedTransitions = ImmutableSet.of(ACTIVE_NOT_IN_SYNC); ABORT_TO_ACTIVE_IN_SYNC.allowedTransitions = ImmutableSet.of(ACTIVE_IN_SYNC); ABORT_TO_ACTIVE_NOT_IN_SYNC.allowedTransitions = ImmutableSet.of(ACTIVE_NOT_IN_SYNC); diff --git a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java index b0ce7f26552..f2697ae719f 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java @@ -145,16 +145,17 @@ public void testMutationBlockingWithMultipleHAGroups() throws Exception { assertFalse(haGroupStoreManager.isMutationBlocked(haGroupName1)); assertFalse(haGroupStoreManager.isMutationBlocked(haGroupName2)); - // Update only second group to ACTIVE_NOT_IN_SYNC_TO_STANDBY + // Move only the second group into a mutation-blocking drain state + // (ACTIVE_IN_SYNC_TO_STANDBY maps to ACTIVE_TO_STANDBY, which blocks mutations). HAGroupStoreRecord transitionRecord2 = new HAGroupStoreRecord("1.0", haGroupName2, - HAGroupStoreRecord.HAGroupState.ACTIVE_NOT_IN_SYNC_TO_STANDBY, 0L, + HAGroupStoreRecord.HAGroupState.ACTIVE_IN_SYNC_TO_STANDBY, 0L, HighAvailabilityPolicy.FAILOVER.toString(), this.peerZKUrl, CLUSTERS.getMasterAddress1(), CLUSTERS.getMasterAddress2(), CLUSTERS.getHdfsUrl1(), CLUSTERS.getHdfsUrl2(), 0L); haAdmin.updateHAGroupStoreRecordInZooKeeper(haGroupName2, transitionRecord2, 0); Thread.sleep(ZK_CURATOR_EVENT_PROPAGATION_TIMEOUT_MS); - // Global mutations should be blocked due to second group + // Blocking is per-group: only the second group is blocked; the first stays writable. assertFalse(haGroupStoreManager.isMutationBlocked(haGroupName1)); assertTrue(haGroupStoreManager.isMutationBlocked(haGroupName2)); } diff --git a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/HAGroupStoreRecordTest.java b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/HAGroupStoreRecordTest.java index 44216ef948f..6750793cfa5 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/HAGroupStoreRecordTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/HAGroupStoreRecordTest.java @@ -239,7 +239,7 @@ public void testHAGroupStateGetClusterRole() { HAGroupStoreRecord.HAGroupState.ACTIVE_IN_SYNC.getClusterRole()); assertEquals(ClusterRoleRecord.ClusterRole.ACTIVE, HAGroupStoreRecord.HAGroupState.ACTIVE_NOT_IN_SYNC.getClusterRole()); - assertEquals(ClusterRoleRecord.ClusterRole.ACTIVE_TO_STANDBY, + assertEquals(ClusterRoleRecord.ClusterRole.ACTIVE, HAGroupStoreRecord.HAGroupState.ACTIVE_NOT_IN_SYNC_TO_STANDBY.getClusterRole()); assertEquals(ClusterRoleRecord.ClusterRole.ACTIVE, HAGroupStoreRecord.HAGroupState.ACTIVE_NOT_IN_SYNC_WITH_OFFLINE_PEER.getClusterRole()); @@ -257,6 +257,12 @@ public void testHAGroupStateGetClusterRole() { HAGroupStoreRecord.HAGroupState.STANDBY_TO_ACTIVE.getClusterRole()); assertEquals(ClusterRoleRecord.ClusterRole.UNKNOWN, HAGroupStoreRecord.HAGroupState.UNKNOWN.getClusterRole()); + + // ANISTS is non-blocking (maps to ACTIVE); only AISTS blocks mutations. + assertFalse(HAGroupStoreRecord.HAGroupState.ACTIVE_NOT_IN_SYNC_TO_STANDBY.getClusterRole() + .isMutationBlocked()); + assertTrue(HAGroupStoreRecord.HAGroupState.ACTIVE_IN_SYNC_TO_STANDBY.getClusterRole() + .isMutationBlocked()); } @Test @@ -294,6 +300,12 @@ public void testHAGroupStateValidTransitions() { .isTransitionAllowed(HAGroupStoreRecord.HAGroupState.ABORT_TO_STANDBY)); assertTrue(HAGroupStoreRecord.HAGroupState.STANDBY_TO_ACTIVE .isTransitionAllowed(HAGroupStoreRecord.HAGroupState.ACTIVE_IN_SYNC)); + + // A degraded standby can recover to STANDBY or promote directly on failover. + assertTrue(HAGroupStoreRecord.HAGroupState.DEGRADED_STANDBY + .isTransitionAllowed(HAGroupStoreRecord.HAGroupState.STANDBY)); + assertTrue(HAGroupStoreRecord.HAGroupState.DEGRADED_STANDBY + .isTransitionAllowed(HAGroupStoreRecord.HAGroupState.STANDBY_TO_ACTIVE)); } @Test