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.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023 024import java.util.concurrent.CompletableFuture; 025import org.apache.hadoop.hbase.HBaseTestingUtil; 026import org.apache.hadoop.hbase.TableName; 027import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 028import org.apache.hadoop.hbase.client.TableDescriptor; 029import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 030import org.apache.hadoop.hbase.master.RegionState; 031import org.apache.hadoop.hbase.master.procedure.CloseExcessRegionReplicasProcedure; 032import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 033import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 034import org.apache.hadoop.hbase.testclassification.MasterTests; 035import org.apache.hadoop.hbase.testclassification.MediumTests; 036import org.junit.jupiter.api.AfterAll; 037import org.junit.jupiter.api.BeforeAll; 038import org.junit.jupiter.api.Tag; 039import org.junit.jupiter.api.Test; 040 041/** 042 * A test to make sure that we will wait for RIT to finish while closing excess region replicas. See 043 * HBASE-28582 and related issues for more details. 044 */ 045@Tag(MasterTests.TAG) 046@Tag(MediumTests.TAG) 047public class TestReduceExcessRegionReplicasBlockedByRIT { 048 049 private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 050 051 private static TableDescriptor TD = 052 TableDescriptorBuilder.newBuilder(TableName.valueOf("CloseExcessRegionReplicas")) 053 .setColumnFamily(ColumnFamilyDescriptorBuilder.of("family")).setRegionReplication(4).build(); 054 055 @BeforeAll 056 public static void setUp() throws Exception { 057 UTIL.startMiniCluster(1); 058 UTIL.getAdmin().createTable(TD); 059 UTIL.waitTableAvailable(TD.getTableName()); 060 UTIL.waitUntilNoRegionsInTransition(); 061 } 062 063 @AfterAll 064 public static void tearDown() throws Exception { 065 UTIL.shutdownMiniCluster(); 066 } 067 068 @Test 069 public void testRIT() throws Exception { 070 RegionStateNode rsn = UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager() 071 .getRegionStates().getTableRegionStateNodes(TD.getTableName()).stream() 072 .filter(rn -> rn.getRegionInfo().getReplicaId() > 1).findAny().get(); 073 // fake a TRSP to block the CloseExcessRegionReplicasProcedure 074 TransitRegionStateProcedure trsp = new TransitRegionStateProcedure(); 075 rsn.setProcedure(trsp); 076 TableDescriptor newTd = TableDescriptorBuilder.newBuilder(TD).setRegionReplication(2).build(); 077 CompletableFuture<Void> future = UTIL.getAsyncConnection().getAdmin().modifyTable(newTd); 078 ProcedureExecutor<MasterProcedureEnv> procExec = 079 UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor(); 080 UTIL.waitFor(5000, () -> procExec.getProcedures().stream() 081 .anyMatch(p -> p instanceof CloseExcessRegionReplicasProcedure && !p.isFinished())); 082 CloseExcessRegionReplicasProcedure proc = 083 procExec.getProcedures().stream().filter(p -> p instanceof CloseExcessRegionReplicasProcedure) 084 .map(p -> (CloseExcessRegionReplicasProcedure) p).findFirst().get(); 085 // make sure that the procedure can not finish 086 for (int i = 0; i < 5; i++) { 087 Thread.sleep(3000); 088 assertFalse(proc.isFinished()); 089 } 090 assertTrue(rsn.isInState(RegionState.State.OPEN)); 091 // unset the procedure, so we could make progress on CloseExcessRegionReplicasProcedure 092 rsn.unsetProcedure(trsp); 093 UTIL.waitFor(60000, () -> proc.isFinished()); 094 095 future.get(); 096 097 // the region should be in CLOSED state, and should have been removed from AM 098 assertTrue(rsn.isInState(RegionState.State.CLOSED)); 099 // only 2 replicas now 100 assertEquals(2, UTIL.getMiniHBaseCluster().getRegions(TD.getTableName()).size()); 101 } 102}