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.client; 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; 023import static org.junit.jupiter.api.Assertions.fail; 024 025import java.io.IOException; 026import java.util.List; 027import java.util.concurrent.ExecutionException; 028import java.util.concurrent.Future; 029import java.util.concurrent.TimeUnit; 030import org.apache.hadoop.hbase.DoNotRetryIOException; 031import org.apache.hadoop.hbase.HBaseTestingUtil; 032import org.apache.hadoop.hbase.ServerName; 033import org.apache.hadoop.hbase.TableName; 034import org.apache.hadoop.hbase.master.assignment.AssignmentTestingUtil; 035import org.apache.hadoop.hbase.master.assignment.SplitTableRegionProcedure; 036import org.apache.hadoop.hbase.master.procedure.DeleteTableProcedure; 037import org.apache.hadoop.hbase.master.procedure.DisableTableProcedure; 038import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 039import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 040import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 041import org.apache.hadoop.hbase.testclassification.ClientTests; 042import org.apache.hadoop.hbase.testclassification.MediumTests; 043import org.apache.hadoop.hbase.util.Bytes; 044import org.apache.hadoop.hbase.util.Threads; 045import org.junit.jupiter.api.AfterEach; 046import org.junit.jupiter.api.BeforeEach; 047import org.junit.jupiter.api.Disabled; 048import org.junit.jupiter.api.Tag; 049import org.junit.jupiter.api.Test; 050import org.junit.jupiter.api.TestInfo; 051 052@Tag(MediumTests.TAG) 053@Tag(ClientTests.TAG) 054public class TestSplitOrMergeStatus { 055 056 private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 057 private static byte[] FAMILY = Bytes.toBytes("testFamily"); 058 059 private String methodName; 060 061 @BeforeEach 062 public void setUp(TestInfo info) throws Exception { 063 methodName = info.getTestMethod().get().getName(); 064 TEST_UTIL.startMiniCluster(2); 065 } 066 067 @AfterEach 068 public void tearDown() throws Exception { 069 TEST_UTIL.shutdownMiniCluster(); 070 } 071 072 @Test 073 public void testSplitSwitch() throws Exception { 074 final TableName tableName = TableName.valueOf(methodName); 075 Table t = TEST_UTIL.createTable(tableName, FAMILY); 076 TEST_UTIL.loadTable(t, FAMILY, false); 077 078 RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(t.getName()); 079 int originalCount = locator.getAllRegionLocations().size(); 080 081 Admin admin = TEST_UTIL.getAdmin(); 082 initSwitchStatus(admin); 083 assertTrue(admin.splitSwitch(false, false)); 084 try { 085 admin.split(t.getName()); 086 fail("Shouldn't get here"); 087 } catch (DoNotRetryIOException dnioe) { 088 // Expected 089 } 090 int count = admin.getRegions(tableName).size(); 091 assertTrue(originalCount == count); 092 assertFalse(admin.splitSwitch(true, false)); 093 admin.split(t.getName()); 094 while ((count = admin.getRegions(tableName).size()) == originalCount) { 095 Threads.sleep(1); 096 } 097 count = admin.getRegions(tableName).size(); 098 assertTrue(originalCount < count); 099 admin.close(); 100 } 101 102 @Disabled 103 @Test 104 public void testMergeSwitch() throws Exception { 105 final TableName tableName = TableName.valueOf(methodName); 106 Table t = TEST_UTIL.createTable(tableName, FAMILY); 107 TEST_UTIL.loadTable(t, FAMILY, false); 108 109 Admin admin = TEST_UTIL.getAdmin(); 110 int originalCount = admin.getRegions(tableName).size(); 111 initSwitchStatus(admin); 112 admin.split(t.getName()); 113 int postSplitCount = -1; 114 while ((postSplitCount = admin.getRegions(tableName).size()) == originalCount) { 115 Threads.sleep(1); 116 } 117 assertTrue(originalCount != postSplitCount, 118 "originalCount=" + originalCount + ", newCount=" + postSplitCount); 119 120 // Merge switch is off so merge should NOT succeed. 121 boolean result = admin.mergeSwitch(false, false); 122 assertTrue(result); 123 List<RegionInfo> regions = admin.getRegions(t.getName()); 124 assertTrue(regions.size() > 1); 125 Future<?> f = admin.mergeRegionsAsync(regions.get(0).getEncodedNameAsBytes(), 126 regions.get(1).getEncodedNameAsBytes(), true); 127 try { 128 f.get(10, TimeUnit.SECONDS); 129 fail("Should not get here."); 130 } catch (ExecutionException ee) { 131 // Expected. 132 } 133 int count = admin.getRegions(tableName).size(); 134 assertTrue(postSplitCount == count, "newCount=" + postSplitCount + ", count=" + count); 135 136 result = admin.mergeSwitch(true, false); 137 regions = admin.getRegions(t.getName()); 138 assertFalse(result); 139 f = admin.mergeRegionsAsync(regions.get(0).getEncodedNameAsBytes(), 140 regions.get(1).getEncodedNameAsBytes(), true); 141 f.get(10, TimeUnit.SECONDS); 142 count = admin.getRegions(tableName).size(); 143 assertTrue((postSplitCount / 2 /* Merge */) == count); 144 admin.close(); 145 } 146 147 @Test 148 public void testMultiSwitches() throws IOException { 149 Admin admin = TEST_UTIL.getAdmin(); 150 assertTrue(admin.splitSwitch(false, false)); 151 assertTrue(admin.mergeSwitch(false, false)); 152 153 assertFalse(admin.isSplitEnabled()); 154 assertFalse(admin.isMergeEnabled()); 155 admin.close(); 156 } 157 158 @Test 159 public void testSplitRegionReplicaRitRecovery() throws Exception { 160 int startRowNum = 11; 161 int rowCount = 60; 162 final TableName tableName = TableName.valueOf(methodName); 163 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 164 TEST_UTIL.getAdmin().createTable(TableDescriptorBuilder.newBuilder(tableName) 165 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)).setRegionReplication(2).build()); 166 TEST_UTIL.waitUntilAllRegionsAssigned(tableName); 167 ServerName serverName = 168 RegionReplicaTestHelper.getRSCarryingReplica(TEST_UTIL, tableName, 1).get(); 169 List<RegionInfo> regions = TEST_UTIL.getAdmin().getRegions(tableName); 170 insertData(tableName, startRowNum, rowCount); 171 int splitRowNum = startRowNum + rowCount / 2; 172 byte[] splitKey = Bytes.toBytes("" + splitRowNum); 173 // Split region of the table 174 long procId = procExec.submitProcedure( 175 new SplitTableRegionProcedure(procExec.getEnvironment(), regions.get(0), splitKey)); 176 // Wait the completion 177 ProcedureTestingUtility.waitProcedure(procExec, procId); 178 // Disable the table 179 long procId1 = procExec 180 .submitProcedure(new DisableTableProcedure(procExec.getEnvironment(), tableName, false)); 181 // Wait the completion 182 ProcedureTestingUtility.waitProcedure(procExec, procId1); 183 // Delete Table 184 long procId2 = 185 procExec.submitProcedure(new DeleteTableProcedure(procExec.getEnvironment(), tableName)); 186 // Wait the completion 187 ProcedureTestingUtility.waitProcedure(procExec, procId2); 188 AssignmentTestingUtil.killRs(TEST_UTIL, serverName); 189 Threads.sleepWithoutInterrupt(5000); 190 boolean hasRegionsInTransition = 191 TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager().hasRegionsInTransition(); 192 assertEquals(false, hasRegionsInTransition); 193 } 194 195 private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() { 196 return TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor(); 197 } 198 199 private void insertData(final TableName tableName, int startRow, int rowCount) 200 throws IOException { 201 Table t = TEST_UTIL.getConnection().getTable(tableName); 202 Put p; 203 for (int i = 0; i < rowCount; i++) { 204 p = new Put(Bytes.toBytes("" + (startRow + i))); 205 p.addColumn(FAMILY, Bytes.toBytes("q1"), Bytes.toBytes(i)); 206 t.put(p); 207 } 208 } 209 210 private void initSwitchStatus(Admin admin) throws IOException { 211 if (!admin.isSplitEnabled()) { 212 admin.splitSwitch(true, false); 213 } 214 if (!admin.isMergeEnabled()) { 215 admin.mergeSwitch(true, false); 216 } 217 assertTrue(admin.isSplitEnabled()); 218 assertTrue(admin.isMergeEnabled()); 219 } 220}