001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.master.assignment; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.util.concurrent.CompletableFuture; 025import org.apache.hadoop.hbase.HBaseClassTestRule; 026import org.apache.hadoop.hbase.HBaseTestingUtil; 027import org.apache.hadoop.hbase.TableName; 028import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 029import org.apache.hadoop.hbase.client.TableDescriptor; 030import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 031import org.apache.hadoop.hbase.master.RegionState; 032import org.apache.hadoop.hbase.master.procedure.CloseExcessRegionReplicasProcedure; 033import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 034import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 035import org.apache.hadoop.hbase.testclassification.MasterTests; 036import org.apache.hadoop.hbase.testclassification.MediumTests; 037import org.junit.AfterClass; 038import org.junit.BeforeClass; 039import org.junit.ClassRule; 040import org.junit.Test; 041import org.junit.experimental.categories.Category; 042 043/** 044 * A test to make sure that we will wait for RIT to finish while closing excess region replicas. See 045 * HBASE-28582 and related issues for more details. 046 */ 047@Category({ MasterTests.class, MediumTests.class }) 048public class TestReduceExcessRegionReplicasBlockedByRIT { 049 050 @ClassRule 051 public static final HBaseClassTestRule CLASS_RULE = 052 HBaseClassTestRule.forClass(TestReduceExcessRegionReplicasBlockedByRIT.class); 053 054 private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 055 056 private static TableDescriptor TD = 057 TableDescriptorBuilder.newBuilder(TableName.valueOf("CloseExcessRegionReplicas")) 058 .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family")).setRegionReplication(4).build(); 059 060 @BeforeClass 061 public static void setUp() throws Exception { 062 UTIL.startMiniCluster(1); 063 UTIL.getAdmin().createTable(TD); 064 UTIL.waitTableAvailable(TD.getTableName()); 065 UTIL.waitUntilNoRegionsInTransition(); 066 } 067 068 @AfterClass 069 public static void tearDown() throws Exception { 070 UTIL.shutdownMiniCluster(); 071 } 072 073 @Test 074 public void testRIT() throws Exception { 075 RegionStateNode rsn = UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager() 076 .getRegionStates().getTableRegionStateNodes(TD.getTableName()).stream() 077 .filter(rn -> rn.getRegionInfo().getReplicaId() > 1).findAny().get(); 078 // fake a TRSP to block the CloseExcessRegionReplicasProcedure 079 TransitRegionStateProcedure trsp = new TransitRegionStateProcedure(); 080 rsn.setProcedure(trsp); 081 TableDescriptor newTd = TableDescriptorBuilder.newBuilder(TD).setRegionReplication(2).build(); 082 CompletableFuture<Void> future = UTIL.getAsyncConnection().getAdmin().modifyTable(newTd); 083 ProcedureExecutor<MasterProcedureEnv> procExec = 084 UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor(); 085 UTIL.waitFor(5000, () -> procExec.getProcedures().stream() 086 .anyMatch(p -> p instanceof CloseExcessRegionReplicasProcedure && !p.isFinished())); 087 CloseExcessRegionReplicasProcedure proc = 088 procExec.getProcedures().stream().filter(p -> p instanceof CloseExcessRegionReplicasProcedure) 089 .map(p -> (CloseExcessRegionReplicasProcedure) p).findFirst().get(); 090 // make sure that the procedure can not finish 091 for (int i = 0; i < 5; i++) { 092 Thread.sleep(3000); 093 assertFalse(proc.isFinished()); 094 } 095 assertTrue(rsn.isInState(RegionState.State.OPEN)); 096 // unset the procedure, so we could make progress on CloseExcessRegionReplicasProcedure 097 rsn.unsetProcedure(trsp); 098 UTIL.waitFor(60000, () -> proc.isFinished()); 099 100 future.get(); 101 102 // the region should be in CLOSED state, and should have been removed from AM 103 assertTrue(rsn.isInState(RegionState.State.CLOSED)); 104 // only 2 replicas now 105 assertEquals(2, UTIL.getMiniHBaseCluster().getRegions(TD.getTableName()).size()); 106 } 107}