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.apache.hadoop.hbase.TableName.META_TABLE_NAME; 021import static org.hamcrest.CoreMatchers.instanceOf; 022import static org.hamcrest.MatcherAssert.assertThat; 023import static org.junit.jupiter.api.Assertions.assertEquals; 024import static org.junit.jupiter.api.Assertions.assertFalse; 025import static org.junit.jupiter.api.Assertions.assertNotEquals; 026import static org.junit.jupiter.api.Assertions.assertNotNull; 027import static org.junit.jupiter.api.Assertions.assertTrue; 028import static org.junit.jupiter.api.Assertions.fail; 029 030import java.io.IOException; 031import java.util.ArrayList; 032import java.util.Collections; 033import java.util.List; 034import java.util.concurrent.ExecutionException; 035import java.util.function.Supplier; 036import java.util.stream.Collectors; 037import org.apache.hadoop.hbase.ClientMetaTableAccessor; 038import org.apache.hadoop.hbase.HBaseParameterizedTestTemplate; 039import org.apache.hadoop.hbase.HConstants; 040import org.apache.hadoop.hbase.HRegionLocation; 041import org.apache.hadoop.hbase.TableName; 042import org.apache.hadoop.hbase.master.HMaster; 043import org.apache.hadoop.hbase.master.assignment.AssignmentTestingUtil; 044import org.apache.hadoop.hbase.master.janitor.CatalogJanitor; 045import org.apache.hadoop.hbase.testclassification.ClientTests; 046import org.apache.hadoop.hbase.testclassification.LargeTests; 047import org.apache.hadoop.hbase.util.Bytes; 048import org.apache.hadoop.hbase.util.Threads; 049import org.junit.jupiter.api.AfterAll; 050import org.junit.jupiter.api.BeforeAll; 051import org.junit.jupiter.api.Disabled; 052import org.junit.jupiter.api.Tag; 053import org.junit.jupiter.api.TestTemplate; 054 055/** 056 * Class to test asynchronous region admin operations. 057 * @see TestAsyncRegionAdminApi This test and it used to be joined it was taking longer than our ten 058 * minute timeout so they were split. 059 */ 060@Tag(LargeTests.TAG) 061@Tag(ClientTests.TAG) 062@HBaseParameterizedTestTemplate(name = "{index}: policy = {0}") 063public class TestAsyncRegionAdminApi2 extends TestAsyncAdminBase { 064 065 public TestAsyncRegionAdminApi2(Supplier<AsyncAdmin> admin) { 066 super(admin); 067 } 068 069 @BeforeAll 070 public static void setUpBeforeClass() throws Exception { 071 TestAsyncAdminBase.setUpBeforeClass(); 072 } 073 074 @AfterAll 075 public static void tearDownAfterClass() throws Exception { 076 TestAsyncAdminBase.tearDownAfterClass(); 077 } 078 079 @TestTemplate 080 public void testGetRegionLocation() throws Exception { 081 RawAsyncHBaseAdmin rawAdmin = (RawAsyncHBaseAdmin) ASYNC_CONN.getAdmin(); 082 TEST_UTIL.createMultiRegionTable(tableName, HConstants.CATALOG_FAMILY); 083 AsyncTableRegionLocator locator = ASYNC_CONN.getRegionLocator(tableName); 084 HRegionLocation regionLocation = locator.getRegionLocation(Bytes.toBytes("mmm")).get(); 085 RegionInfo region = regionLocation.getRegion(); 086 byte[] regionName = regionLocation.getRegion().getRegionName(); 087 HRegionLocation location = rawAdmin.getRegionLocation(regionName).get(); 088 assertTrue(Bytes.equals(regionName, location.getRegion().getRegionName())); 089 location = rawAdmin.getRegionLocation(region.getEncodedNameAsBytes()).get(); 090 assertTrue(Bytes.equals(regionName, location.getRegion().getRegionName())); 091 } 092 093 @TestTemplate 094 public void testSplitSwitch() throws Exception { 095 createTableWithDefaultConf(tableName); 096 byte[][] families = { FAMILY }; 097 final int rows = 10000; 098 TestAsyncRegionAdminApi.loadData(tableName, families, rows); 099 100 AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME); 101 List<HRegionLocation> regionLocations = 102 ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 103 int originalCount = regionLocations.size(); 104 105 initSplitMergeSwitch(); 106 assertTrue(admin.splitSwitch(false).get()); 107 try { 108 admin.split(tableName, Bytes.toBytes(rows / 2)).join(); 109 } catch (Exception e) { 110 // Expected 111 } 112 int count = admin.getRegions(tableName).get().size(); 113 assertTrue(originalCount == count); 114 115 assertFalse(admin.splitSwitch(true).get()); 116 admin.split(tableName).join(); 117 while ((count = admin.getRegions(tableName).get().size()) == originalCount) { 118 Threads.sleep(100); 119 } 120 assertTrue(originalCount < count); 121 } 122 123 @TestTemplate 124 @Disabled 125 // It was ignored in TestSplitOrMergeStatus, too 126 public void testMergeSwitch() throws Exception { 127 createTableWithDefaultConf(tableName); 128 byte[][] families = { FAMILY }; 129 TestAsyncRegionAdminApi.loadData(tableName, families, 1000); 130 131 AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME); 132 List<HRegionLocation> regionLocations = 133 ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 134 int originalCount = regionLocations.size(); 135 136 initSplitMergeSwitch(); 137 admin.split(tableName).join(); 138 int postSplitCount = originalCount; 139 while ((postSplitCount = admin.getRegions(tableName).get().size()) == originalCount) { 140 Threads.sleep(100); 141 } 142 assertTrue(originalCount != postSplitCount, 143 "originalCount=" + originalCount + ", postSplitCount=" + postSplitCount); 144 145 // Merge switch is off so merge should NOT succeed. 146 assertTrue(admin.mergeSwitch(false).get()); 147 List<RegionInfo> regions = admin.getRegions(tableName).get(); 148 assertTrue(regions.size() > 1); 149 admin.mergeRegions(regions.get(0).getRegionName(), regions.get(1).getRegionName(), true).join(); 150 int count = admin.getRegions(tableName).get().size(); 151 assertTrue(postSplitCount == count, "postSplitCount=" + postSplitCount + ", count=" + count); 152 153 // Merge switch is on so merge should succeed. 154 assertFalse(admin.mergeSwitch(true).get()); 155 admin.mergeRegions(regions.get(0).getRegionName(), regions.get(1).getRegionName(), true).join(); 156 count = admin.getRegions(tableName).get().size(); 157 assertTrue((postSplitCount / 2) == count); 158 } 159 160 private void initSplitMergeSwitch() throws Exception { 161 if (!admin.isSplitEnabled().get()) { 162 admin.splitSwitch(true).get(); 163 } 164 if (!admin.isMergeEnabled().get()) { 165 admin.mergeSwitch(true).get(); 166 } 167 assertTrue(admin.isSplitEnabled().get()); 168 assertTrue(admin.isMergeEnabled().get()); 169 } 170 171 @TestTemplate 172 public void testMergeRegions() throws Exception { 173 byte[][] splitRows = new byte[][] { Bytes.toBytes("3"), Bytes.toBytes("6") }; 174 createTableWithDefaultConf(tableName, splitRows); 175 176 AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME); 177 List<HRegionLocation> regionLocations = 178 ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 179 RegionInfo regionA; 180 RegionInfo regionB; 181 RegionInfo regionC; 182 RegionInfo mergedChildRegion = null; 183 184 // merge with full name 185 assertEquals(3, regionLocations.size()); 186 regionA = regionLocations.get(0).getRegion(); 187 regionB = regionLocations.get(1).getRegion(); 188 regionC = regionLocations.get(2).getRegion(); 189 admin.mergeRegions(regionA.getRegionName(), regionB.getRegionName(), false).get(); 190 191 regionLocations = ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 192 193 assertEquals(2, regionLocations.size()); 194 for (HRegionLocation rl : regionLocations) { 195 if (regionC.compareTo(rl.getRegion()) != 0) { 196 mergedChildRegion = rl.getRegion(); 197 break; 198 } 199 } 200 201 assertNotNull(mergedChildRegion); 202 // Need to wait GC for merged child region is done. 203 HMaster services = TEST_UTIL.getHBaseCluster().getMaster(); 204 CatalogJanitor cj = services.getCatalogJanitor(); 205 assertTrue(cj.scan() > 0); 206 // Wait until all procedures settled down 207 while (!services.getMasterProcedureExecutor().getActiveProcIds().isEmpty()) { 208 Thread.sleep(200); 209 } 210 // merge with encoded name 211 admin.mergeRegions(regionC.getRegionName(), mergedChildRegion.getRegionName(), false).get(); 212 213 regionLocations = ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 214 assertEquals(1, regionLocations.size()); 215 } 216 217 @TestTemplate 218 public void testMergeRegionsInvalidRegionCount() throws Exception { 219 byte[][] splitRows = new byte[][] { Bytes.toBytes("3"), Bytes.toBytes("6") }; 220 createTableWithDefaultConf(tableName, splitRows); 221 List<RegionInfo> regions = admin.getRegions(tableName).join(); 222 // 0 223 try { 224 admin.mergeRegions(Collections.emptyList(), false).get(); 225 fail(); 226 } catch (ExecutionException e) { 227 // expected 228 assertThat(e.getCause(), instanceOf(IllegalArgumentException.class)); 229 } 230 // 1 231 try { 232 admin.mergeRegions(regions.stream().limit(1).map(RegionInfo::getEncodedNameAsBytes) 233 .collect(Collectors.toList()), false).get(); 234 fail(); 235 } catch (ExecutionException e) { 236 // expected 237 assertThat(e.getCause(), instanceOf(IllegalArgumentException.class)); 238 } 239 } 240 241 @TestTemplate 242 public void testSplitTable() throws Exception { 243 initSplitMergeSwitch(); 244 splitTest(TableName.valueOf("testSplitTable"), 3000, false, null); 245 splitTest(TableName.valueOf("testSplitTableWithSplitPoint"), 3000, false, Bytes.toBytes("3")); 246 splitTest(TableName.valueOf("testSplitTableRegion"), 3000, true, null); 247 splitTest(TableName.valueOf("testSplitTableRegionWithSplitPoint2"), 3000, true, 248 Bytes.toBytes("3")); 249 } 250 251 private void splitTest(TableName tableName, int rowCount, boolean isSplitRegion, 252 byte[] splitPoint) throws Exception { 253 // create table 254 createTableWithDefaultConf(tableName); 255 256 AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME); 257 List<HRegionLocation> regionLocations = 258 ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 259 assertEquals(1, regionLocations.size()); 260 261 AsyncTable<?> table = ASYNC_CONN.getTable(tableName); 262 List<Put> puts = new ArrayList<>(); 263 for (int i = 0; i < rowCount; i++) { 264 Put put = new Put(Bytes.toBytes(i)); 265 put.addColumn(FAMILY, null, Bytes.toBytes("value" + i)); 266 puts.add(put); 267 } 268 table.putAll(puts).join(); 269 270 if (isSplitRegion) { 271 if (splitPoint == null) { 272 admin.splitRegion(regionLocations.get(0).getRegion().getRegionName()).get(); 273 } else { 274 admin.splitRegion(regionLocations.get(0).getRegion().getRegionName(), splitPoint).get(); 275 } 276 } else { 277 if (splitPoint == null) { 278 admin.split(tableName).get(); 279 } else { 280 admin.split(tableName, splitPoint).get(); 281 } 282 } 283 284 int count = 0; 285 for (int i = 0; i < 45; i++) { 286 try { 287 regionLocations = 288 ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 289 count = regionLocations.size(); 290 if (count >= 2) { 291 break; 292 } 293 Thread.sleep(1000L); 294 } catch (Exception e) { 295 LOG.error(e.toString(), e); 296 } 297 } 298 assertEquals(2, count); 299 } 300 301 @TestTemplate 302 public void testTruncateRegion() throws Exception { 303 // Arrange - Create table, insert data, identify region to truncate. 304 final byte[][] splitKeys = 305 new byte[][] { Bytes.toBytes("30"), Bytes.toBytes("60"), Bytes.toBytes("90") }; 306 String family1 = "f1"; 307 String family2 = "f2"; 308 309 final String[] sFamilies = new String[] { family1, family2 }; 310 final byte[][] bFamilies = new byte[][] { Bytes.toBytes(family1), Bytes.toBytes(family2) }; 311 createTableWithDefaultConf(tableName, splitKeys, bFamilies); 312 313 AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME); 314 List<HRegionLocation> regionLocations = 315 ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 316 RegionInfo regionToBeTruncated = regionLocations.get(0).getRegion(); 317 318 assertEquals(4, regionLocations.size()); 319 320 AssignmentTestingUtil.insertData(TEST_UTIL, tableName, 2, 21, sFamilies); 321 AssignmentTestingUtil.insertData(TEST_UTIL, tableName, 2, 31, sFamilies); 322 AssignmentTestingUtil.insertData(TEST_UTIL, tableName, 2, 61, sFamilies); 323 AssignmentTestingUtil.insertData(TEST_UTIL, tableName, 2, 91, sFamilies); 324 int rowCountBeforeTruncate = TEST_UTIL.countRows(tableName); 325 326 // Act - Truncate the first region 327 admin.truncateRegion(regionToBeTruncated.getRegionName()).get(); 328 329 // Assert 330 int rowCountAfterTruncate = TEST_UTIL.countRows(tableName); 331 assertNotEquals(rowCountBeforeTruncate, rowCountAfterTruncate); 332 int expectedRowCount = rowCountBeforeTruncate - 2;// Since region with 2 rows was truncated. 333 assertEquals(expectedRowCount, rowCountAfterTruncate); 334 } 335 336 @TestTemplate 337 public void testTruncateReplicaRegionNotAllowed() throws Exception { 338 // Arrange - Create table, insert data, identify region to truncate. 339 final byte[][] splitKeys = 340 new byte[][] { Bytes.toBytes("30"), Bytes.toBytes("60"), Bytes.toBytes("90") }; 341 String family1 = "f1"; 342 String family2 = "f2"; 343 344 final byte[][] bFamilies = new byte[][] { Bytes.toBytes(family1), Bytes.toBytes(family2) }; 345 createTableWithDefaultConf(tableName, 2, splitKeys, bFamilies); 346 347 AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME); 348 List<HRegionLocation> regionLocations = 349 ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 350 RegionInfo primaryRegion = regionLocations.get(0).getRegion(); 351 352 RegionInfo firstReplica = RegionReplicaUtil.getRegionInfoForReplica(primaryRegion, 1); 353 354 // Act - Truncate the first region 355 try { 356 admin.truncateRegion(firstReplica.getRegionName()).get(); 357 } catch (Exception e) { 358 // Assert 359 assertEquals( 360 "Can't truncate replicas directly.Replicas are auto-truncated " 361 + "when their primary is truncated.", 362 e.getCause().getMessage(), "Expected message is different"); 363 } 364 } 365 366 @TestTemplate 367 public void testTruncateRegionsMetaTableRegionsNotAllowed() throws Exception { 368 AsyncTableRegionLocator locator = ASYNC_CONN.getRegionLocator(META_TABLE_NAME); 369 List<HRegionLocation> regionLocations = locator.getAllRegionLocations().get(); 370 HRegionLocation regionToBeTruncated = regionLocations.get(0); 371 // 1 372 try { 373 admin.truncateRegion(regionToBeTruncated.getRegion().getRegionName()).get(); 374 fail(); 375 } catch (ExecutionException e) { 376 // expected 377 assertThat(e.getCause(), instanceOf(IOException.class)); 378 assertEquals("Can't truncate region in catalog tables", e.getCause().getMessage()); 379 } 380 } 381}