From bfb2971b8e16fbe8e83ae4ee11f7aa53e46e4b8c Mon Sep 17 00:00:00 2001 From: Tanya Stickles Date: Thu, 24 Sep 2026 11:08:39 -0400 Subject: [PATCH 1/7] HBASE-30353 Prevent split parent regions from being re-opened after master failover Split parents are permanently retired regions and must never be re-assigned. Two-part fix: 1. RegionStateStore.splitRegion() now writes SPLIT into info:state for the parent so that after a master failover, loadMeta reconstructs the parent with state=SPLIT rather than falling back to OFFLINE (the null-state path), which would make it eligible for re-assignment via processOfflineRegions(). 2. AssignmentManager guards isSplit() in both preTransitCheck (normal assign path) and createAssignProcedure (HBCK2/override/force path). The guard in createAssignProcedure sits before the override branch so that even force=true cannot bypass it. Together these cover the live-state case (state=SPLIT) and the post-failover case where an older meta row carries no info:state column and regionInfo is reconstructed with state=CLOSED but isSplit()=true. Co-Authored-By: Claude Sonnet 4.6 --- .../master/assignment/AssignmentManager.java | 20 +++++++++++++ .../master/assignment/RegionStateStore.java | 5 ++++ .../assignment/TestAssignmentManager.java | 28 +++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java index 5baf30846e08..cb4413141a35 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java @@ -768,6 +768,13 @@ private void preTransitCheck(RegionStateNode regionNode, RegionState.State[] exp if (!regionNode.isInState(expectedStates)) { throw new DoNotRetryRegionException(UNEXPECTED_STATE_REGION + regionNode); } + // A split parent is permanently retired — it must never be re-opened. Guard both the normal + // assign path (state=SPLIT) and the post-failover path where loadMeta may have reconstructed + // the node with state=CLOSED but regionInfo.isSplit()=true. See HBASE-30353. + if (regionNode.isSplit()) { + throw new DoNotRetryRegionException( + regionNode.getRegionInfo().getEncodedName() + " is a split parent and cannot be assigned"); + } if (isTableDisabled(regionNode.getTable())) { throw new DoNotRetryIOException(regionNode.getTable() + " is disabled for " + regionNode); } @@ -786,6 +793,13 @@ private TransitRegionStateProcedure createAssignProcedure(RegionInfo regionInfo, RegionStateNode regionNode = regionStates.getOrCreateRegionStateNode(regionInfo); regionNode.lock(); try { + // Guard the override (HBCK2) path too: even with override=true or force=true a split parent + // must never be assigned. preTransitCheck is skipped when force=true, so this check must + // come first. See HBASE-30353. + if (regionNode.isSplit()) { + throw new DoNotRetryRegionException(regionNode.getRegionInfo().getEncodedName() + + " is a split parent and cannot be assigned"); + } if (override) { if (!force) { preTransitCheck(regionNode, STATES_EXPECTED_ON_ASSIGN); @@ -1812,6 +1826,12 @@ public void joinCluster() throws IOException { // Public so can be run by the Master as part of the startup. Needs hbase:meta to be online. // Needs to be done after the table state manager has been started. public void processOfflineRegions() { + // This method calls TransitRegionStateProcedure.assign() directly, bypassing both + // preTransitCheck and createAssignProcedure (and their isSplit() guards). Split parents are + // safe here only because RegionStateStore.splitRegion() writes info:state=SPLIT to meta, so + // loadMeta reconstructs them with state=SPLIT rather than falling through the null-state + // fallback to OFFLINE. If that meta write were ever removed, split parents could enter this + // path unchecked and be re-opened. See HBASE-30353. TransitRegionStateProcedure[] procs = regionStates.getRegionStateNodes().stream().filter(rsn -> rsn.isInState(State.OFFLINE)) .filter(rsn -> isTableEnabled(rsn.getRegionInfo().getTable())).map(rsn -> { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStateStore.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStateStore.java index 6dac4c37c1b3..17674d9c5f5f 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStateStore.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStateStore.java @@ -376,6 +376,11 @@ public void splitRegion(RegionInfo parent, RegionInfo splitA, RegionInfo splitB, Put putParent = MetaTableAccessor.makePutFromRegionInfo( RegionInfoBuilder.newBuilder(parent).setOffline(true).setSplit(true).build(), time); MetaTableAccessor.addDaughtersToPut(putParent, splitA, splitB); + // Persist the terminal SPLIT state so that after a master failover loadMeta reconstructs the + // parent as state=SPLIT rather than falling back to OFFLINE, which would make it eligible for + // re-assignment via processOfflineRegions. See HBASE-30353. + MetaTableAccessor.addRegionStateToPut(putParent, RegionInfo.DEFAULT_REPLICA_ID, + RegionState.State.SPLIT); // Puts for daughters Put putA = MetaTableAccessor.makePutFromRegionInfo(splitA, time); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java index 01d051779058..b5f87f2bab83 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java @@ -20,11 +20,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import org.apache.hadoop.hbase.DoNotRetryIOException; import org.apache.hadoop.hbase.HBaseTestingUtil; import org.apache.hadoop.hbase.MetaTableAccessor; import org.apache.hadoop.hbase.TableName; @@ -327,4 +329,30 @@ public void testLoadRegionFromMetaRegionNotInMeta() throws Exception { this.util.killMiniHBaseCluster(); } } + + @Test + public void testSplitParentCannotBeAssignedWhenStateIsClosedAfterFailover() throws Exception { + // Simulate the post-failover condition: regionInfo.isSplit()=true but state=CLOSED because the + // old code path did not write SPLIT to info:state. See HBASE-30353. + RegionInfo splitParent = RegionInfoBuilder.newBuilder(TableName.valueOf("test-split-closed")) + .setSplit(true).setOffline(true).build(); + RegionStateNode rsn = am.getRegionStates().getOrCreateRegionStateNode(splitParent); + rsn.setState(State.CLOSED); + + assertThrows(DoNotRetryIOException.class, () -> am.assign(splitParent)); + assertNull(am.createOneAssignProcedure(splitParent, true, false)); + } + + @Test + public void testSplitParentCannotBeAssignedWhenStateIsSplit() throws Exception { + // Simulate the normal post-fix state: regionInfo.isSplit()=true and state=SPLIT (written to + // info:state by RegionStateStore.splitRegion). See HBASE-30353. + RegionInfo splitParent = RegionInfoBuilder.newBuilder(TableName.valueOf("test-split-split")) + .setSplit(true).setOffline(true).build(); + RegionStateNode rsn = am.getRegionStates().getOrCreateRegionStateNode(splitParent); + rsn.setState(State.SPLIT); + + assertThrows(DoNotRetryIOException.class, () -> am.assign(splitParent)); + assertNull(am.createOneAssignProcedure(splitParent, true, false)); + } } From af158e125048d532551b23d51f508c842da3169c Mon Sep 17 00:00:00 2001 From: Tanya Stickles Date: Thu, 24 Sep 2026 13:55:32 -0400 Subject: [PATCH 2/7] updating comments --- .../master/assignment/AssignmentManager.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java index cb4413141a35..f198129f169d 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java @@ -768,9 +768,12 @@ private void preTransitCheck(RegionStateNode regionNode, RegionState.State[] exp if (!regionNode.isInState(expectedStates)) { throw new DoNotRetryRegionException(UNEXPECTED_STATE_REGION + regionNode); } - // A split parent is permanently retired — it must never be re-opened. Guard both the normal - // assign path (state=SPLIT) and the post-failover path where loadMeta may have reconstructed - // the node with state=CLOSED but regionInfo.isSplit()=true. See HBASE-30353. + // if we don't write the state as SPLIT to the meta, we would need this check. + + // if we want to write the state as SPLIT to the meta, tbd if we would need this. + // we would want this to be safe as we roll out the change. there may be some regionservers that + // are split but don't have the SPLIT state persisted to the meta because they split before this + // change. the actual chances of that happening are low (but 0). if (regionNode.isSplit()) { throw new DoNotRetryRegionException( regionNode.getRegionInfo().getEncodedName() + " is a split parent and cannot be assigned"); @@ -793,9 +796,10 @@ private TransitRegionStateProcedure createAssignProcedure(RegionInfo regionInfo, RegionStateNode regionNode = regionStates.getOrCreateRegionStateNode(regionInfo); regionNode.lock(); try { - // Guard the override (HBCK2) path too: even with override=true or force=true a split parent - // must never be assigned. preTransitCheck is skipped when force=true, so this check must - // come first. See HBASE-30353. + // we'd still need this check. + // if createAssignProcedure is called with override=true, it skips the preTansitCheck that + // + // would validate the state (checks if it's CLOSED or OFFLINE) if (regionNode.isSplit()) { throw new DoNotRetryRegionException(regionNode.getRegionInfo().getEncodedName() + " is a split parent and cannot be assigned"); From ac031eb3deea4e38d9e028f4ae883e044548e2ba Mon Sep 17 00:00:00 2001 From: Tanya Stickles Date: Thu, 24 Sep 2026 14:24:08 -0400 Subject: [PATCH 3/7] adding another comment --- .../hadoop/hbase/master/assignment/AssignmentManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java index f198129f169d..11794fe7c770 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java @@ -1838,6 +1838,8 @@ public void processOfflineRegions() { // path unchecked and be re-opened. See HBASE-30353. TransitRegionStateProcedure[] procs = regionStates.getRegionStateNodes().stream().filter(rsn -> rsn.isInState(State.OFFLINE)) + // if we don't persist the SPLIT state, we'll need to add an extra filter here. + // .filter(rsn -> !rsn.getRegionInfo().isSplit()) .filter(rsn -> isTableEnabled(rsn.getRegionInfo().getTable())).map(rsn -> { rsn.lock(); try { From df28eda47d304feb656f43c1557bc8b40c65eaa3 Mon Sep 17 00:00:00 2001 From: Tanya Stickles Date: Fri, 25 Sep 2026 14:02:04 -0400 Subject: [PATCH 4/7] cleaning up -- removing comments from discussion --- .../master/assignment/AssignmentManager.java | 17 ----------------- .../master/assignment/RegionStateStore.java | 3 --- 2 files changed, 20 deletions(-) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java index 11794fe7c770..bb9ea98076b3 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java @@ -768,16 +768,7 @@ private void preTransitCheck(RegionStateNode regionNode, RegionState.State[] exp if (!regionNode.isInState(expectedStates)) { throw new DoNotRetryRegionException(UNEXPECTED_STATE_REGION + regionNode); } - // if we don't write the state as SPLIT to the meta, we would need this check. - // if we want to write the state as SPLIT to the meta, tbd if we would need this. - // we would want this to be safe as we roll out the change. there may be some regionservers that - // are split but don't have the SPLIT state persisted to the meta because they split before this - // change. the actual chances of that happening are low (but 0). - if (regionNode.isSplit()) { - throw new DoNotRetryRegionException( - regionNode.getRegionInfo().getEncodedName() + " is a split parent and cannot be assigned"); - } if (isTableDisabled(regionNode.getTable())) { throw new DoNotRetryIOException(regionNode.getTable() + " is disabled for " + regionNode); } @@ -1830,16 +1821,8 @@ public void joinCluster() throws IOException { // Public so can be run by the Master as part of the startup. Needs hbase:meta to be online. // Needs to be done after the table state manager has been started. public void processOfflineRegions() { - // This method calls TransitRegionStateProcedure.assign() directly, bypassing both - // preTransitCheck and createAssignProcedure (and their isSplit() guards). Split parents are - // safe here only because RegionStateStore.splitRegion() writes info:state=SPLIT to meta, so - // loadMeta reconstructs them with state=SPLIT rather than falling through the null-state - // fallback to OFFLINE. If that meta write were ever removed, split parents could enter this - // path unchecked and be re-opened. See HBASE-30353. TransitRegionStateProcedure[] procs = regionStates.getRegionStateNodes().stream().filter(rsn -> rsn.isInState(State.OFFLINE)) - // if we don't persist the SPLIT state, we'll need to add an extra filter here. - // .filter(rsn -> !rsn.getRegionInfo().isSplit()) .filter(rsn -> isTableEnabled(rsn.getRegionInfo().getTable())).map(rsn -> { rsn.lock(); try { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStateStore.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStateStore.java index 17674d9c5f5f..8b2003bfb207 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStateStore.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStateStore.java @@ -376,9 +376,6 @@ public void splitRegion(RegionInfo parent, RegionInfo splitA, RegionInfo splitB, Put putParent = MetaTableAccessor.makePutFromRegionInfo( RegionInfoBuilder.newBuilder(parent).setOffline(true).setSplit(true).build(), time); MetaTableAccessor.addDaughtersToPut(putParent, splitA, splitB); - // Persist the terminal SPLIT state so that after a master failover loadMeta reconstructs the - // parent as state=SPLIT rather than falling back to OFFLINE, which would make it eligible for - // re-assignment via processOfflineRegions. See HBASE-30353. MetaTableAccessor.addRegionStateToPut(putParent, RegionInfo.DEFAULT_REPLICA_ID, RegionState.State.SPLIT); From a25c826f31794d5aef063ab560f56ec87119bc12 Mon Sep 17 00:00:00 2001 From: Tanya Stickles Date: Fri, 25 Sep 2026 14:02:43 -0400 Subject: [PATCH 5/7] removing more comments --- .../hadoop/hbase/master/assignment/AssignmentManager.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java index bb9ea98076b3..12c2e1799cf1 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/AssignmentManager.java @@ -787,10 +787,6 @@ private TransitRegionStateProcedure createAssignProcedure(RegionInfo regionInfo, RegionStateNode regionNode = regionStates.getOrCreateRegionStateNode(regionInfo); regionNode.lock(); try { - // we'd still need this check. - // if createAssignProcedure is called with override=true, it skips the preTansitCheck that - // - // would validate the state (checks if it's CLOSED or OFFLINE) if (regionNode.isSplit()) { throw new DoNotRetryRegionException(regionNode.getRegionInfo().getEncodedName() + " is a split parent and cannot be assigned"); From e07af6505dfaadae3aac5bd6d3e133c7cbdd91a8 Mon Sep 17 00:00:00 2001 From: Tanya Stickles Date: Fri, 25 Sep 2026 14:07:33 -0400 Subject: [PATCH 6/7] cleaning up tests --- .../assignment/TestAssignmentManager.java | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java index b5f87f2bab83..f2ba5a7fc49b 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java @@ -219,6 +219,17 @@ public void testUnassignAnUnassignedRegion() throws Exception { assertEquals(unassignFailedCount, unassignProcMetrics.getFailedCounter().getCount()); } + @Test + public void testAssignThrowsWithASplitParent() throws Exception { + RegionInfo splitParent = RegionInfoBuilder.newBuilder(TableName.valueOf("test-split-split")) + .setSplit(true).setOffline(true).build(); + RegionStateNode rsn = am.getRegionStates().getOrCreateRegionStateNode(splitParent); + rsn.setState(State.SPLIT); + + assertThrows(DoNotRetryIOException.class, () -> am.assign(splitParent)); + assertNull(am.createOneAssignProcedure(splitParent, true, false)); + } + /** * It is possible that when AM send assign meta request to a RS successfully, but RS can not send * back any response, which cause master startup hangs forever @@ -329,30 +340,4 @@ public void testLoadRegionFromMetaRegionNotInMeta() throws Exception { this.util.killMiniHBaseCluster(); } } - - @Test - public void testSplitParentCannotBeAssignedWhenStateIsClosedAfterFailover() throws Exception { - // Simulate the post-failover condition: regionInfo.isSplit()=true but state=CLOSED because the - // old code path did not write SPLIT to info:state. See HBASE-30353. - RegionInfo splitParent = RegionInfoBuilder.newBuilder(TableName.valueOf("test-split-closed")) - .setSplit(true).setOffline(true).build(); - RegionStateNode rsn = am.getRegionStates().getOrCreateRegionStateNode(splitParent); - rsn.setState(State.CLOSED); - - assertThrows(DoNotRetryIOException.class, () -> am.assign(splitParent)); - assertNull(am.createOneAssignProcedure(splitParent, true, false)); - } - - @Test - public void testSplitParentCannotBeAssignedWhenStateIsSplit() throws Exception { - // Simulate the normal post-fix state: regionInfo.isSplit()=true and state=SPLIT (written to - // info:state by RegionStateStore.splitRegion). See HBASE-30353. - RegionInfo splitParent = RegionInfoBuilder.newBuilder(TableName.valueOf("test-split-split")) - .setSplit(true).setOffline(true).build(); - RegionStateNode rsn = am.getRegionStates().getOrCreateRegionStateNode(splitParent); - rsn.setState(State.SPLIT); - - assertThrows(DoNotRetryIOException.class, () -> am.assign(splitParent)); - assertNull(am.createOneAssignProcedure(splitParent, true, false)); - } } From 7aec8bc579ae947d01755f475764af92599618e8 Mon Sep 17 00:00:00 2001 From: Tanya Stickles Date: Fri, 25 Sep 2026 15:10:56 -0400 Subject: [PATCH 7/7] adding some unit tests --- .../assignment/TestAssignmentManager.java | 15 +++++++++ .../assignment/TestRegionStateStore.java | 32 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java index f2ba5a7fc49b..fe8d103ae830 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestAssignmentManager.java @@ -230,6 +230,21 @@ public void testAssignThrowsWithASplitParent() throws Exception { assertNull(am.createOneAssignProcedure(splitParent, true, false)); } + // Simulate the pre-fix failover scenario: regionInfo.isSplit()=true but state=OFFLINE because + // the old code path did not write SPLIT to info:state, causing loadMeta to fall back to OFFLINE. + // See HBASE-30353. + @Test + public void testAssignThrowsWithASplitParentInOfflineState() throws Exception { + RegionInfo splitParent = + RegionInfoBuilder.newBuilder(TableName.valueOf("test-split-offline")).setSplit(true) + .setOffline(true).build(); + RegionStateNode rsn = am.getRegionStates().getOrCreateRegionStateNode(splitParent); + rsn.setState(State.OFFLINE); + + assertThrows(DoNotRetryIOException.class, () -> am.assign(splitParent)); + assertNull(am.createOneAssignProcedure(splitParent, true, false)); + } + /** * It is possible that when AM send assign meta request to a RS successfully, but RS can not send * back any response, which cause master startup hangs forever diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestRegionStateStore.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestRegionStateStore.java index b85b7c606063..1c9870eb1058 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestRegionStateStore.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestRegionStateStore.java @@ -221,6 +221,38 @@ public void testEmptyMetaDaughterLocationDuringSplit() throws IOException { } } + + @Test + public void testSplitRegionWritesSplitStateForParentInMeta() throws IOException { + long regionId = EnvironmentEdgeManager.currentTime(); + ServerName serverName = ServerName.valueOf("foo", 60010, ThreadLocalRandom.current().nextLong()); + TableName tableName = name.getTableName(); + RegionInfo parent = RegionInfoBuilder.newBuilder(tableName) + .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false) + .setRegionId(regionId).setReplicaId(0).build(); + RegionInfo splitA = RegionInfoBuilder.newBuilder(tableName) + .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(Bytes.toBytes("a")).setSplit(false) + .setRegionId(regionId + 1).setReplicaId(0).build(); + RegionInfo splitB = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("a")) + .setEndKey(HConstants.EMPTY_END_ROW).setSplit(false).setRegionId(regionId + 1).setReplicaId(0) + .build(); + MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), Lists.newArrayList(parent), 1); + final RegionStateStore regionStateStore = + UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore(); + regionStateStore.splitRegion(parent, splitA, splitB, serverName, + TableDescriptorBuilder.newBuilder(tableName).build()); + + + try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) { + Result result = meta.get(new Get(parent.getRegionName())); + Cell stateCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, + CatalogFamilyFormat.getRegionStateColumn(RegionInfo.DEFAULT_REPLICA_ID)); + assertNotNull(stateCell); + assertEquals(RegionState.State.SPLIT.name(), Bytes.toString(stateCell.getValueArray(), + stateCell.getValueOffset(), stateCell.getValueLength())); + } + } + @Test public void testMetaLocationForRegionReplicasIsAddedAtRegionMerge() throws IOException { long regionId = EnvironmentEdgeManager.currentTime();