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.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotEquals; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import java.util.ArrayList; 028import java.util.List; 029import java.util.concurrent.ExecutionException; 030import java.util.concurrent.atomic.AtomicInteger; 031import java.util.stream.Collectors; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.ServerName; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.master.HMaster; 036import org.apache.hadoop.hbase.master.NoSuchProcedureException; 037import org.apache.hadoop.hbase.master.RegionState; 038import org.apache.hadoop.hbase.master.ServerManager; 039import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 040import org.apache.hadoop.hbase.master.assignment.RegionStates; 041import org.apache.hadoop.hbase.regionserver.HRegionServer; 042import org.apache.hadoop.hbase.regionserver.Region; 043import org.apache.hadoop.hbase.testclassification.ClientTests; 044import org.apache.hadoop.hbase.testclassification.LargeTests; 045import org.apache.hadoop.hbase.util.Bytes; 046import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 047import org.apache.hadoop.hbase.util.JVMClusterUtil; 048import org.apache.hadoop.hbase.util.Threads; 049import org.junit.ClassRule; 050import org.junit.Test; 051import org.junit.experimental.categories.Category; 052import org.junit.runner.RunWith; 053import org.junit.runners.Parameterized; 054 055/** 056 * Class to test asynchronous region admin operations. 057 * @see TestAsyncRegionAdminApi2 This test and it used to be joined it was taking longer than our 058 * ten minute timeout so they were split. 059 */ 060@RunWith(Parameterized.class) 061@Category({ LargeTests.class, ClientTests.class }) 062public class TestAsyncRegionAdminApi extends TestAsyncAdminBase { 063 @ClassRule 064 public static final HBaseClassTestRule CLASS_RULE = 065 HBaseClassTestRule.forClass(TestAsyncRegionAdminApi.class); 066 067 @Test 068 public void testAssignRegionAndUnassignRegion() throws Exception { 069 createTableWithDefaultConf(tableName); 070 071 // assign region. 072 HMaster master = TEST_UTIL.getHBaseCluster().getMaster(); 073 AssignmentManager am = master.getAssignmentManager(); 074 RegionInfo hri = am.getRegionStates().getRegionsOfTable(tableName).get(0); 075 076 // assert region on server 077 RegionStates regionStates = am.getRegionStates(); 078 ServerName serverName = regionStates.getRegionServerOfRegion(hri); 079 TEST_UTIL.assertRegionOnServer(hri, serverName, 200); 080 assertTrue(regionStates.getRegionState(hri).isOpened()); 081 082 // Region is assigned now. Let's assign it again. 083 // Master should not abort, and region should stay assigned. 084 admin.assign(hri.getRegionName()).get(); 085 try { 086 am.waitForAssignment(hri); 087 fail("Expected NoSuchProcedureException"); 088 } catch (NoSuchProcedureException e) { 089 // Expected 090 } 091 assertTrue(regionStates.getRegionState(hri).isOpened()); 092 093 // unassign region 094 admin.unassign(hri.getRegionName(), true).get(); 095 try { 096 am.waitForAssignment(hri); 097 fail("Expected NoSuchProcedureException"); 098 } catch (NoSuchProcedureException e) { 099 // Expected 100 } 101 assertTrue(regionStates.getRegionState(hri).isClosed()); 102 } 103 104 RegionInfo createTableAndGetOneRegion(final TableName tableName) 105 throws IOException, InterruptedException, ExecutionException { 106 TableDescriptor desc = 107 TableDescriptorBuilder.newBuilder(tableName) 108 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)).build(); 109 admin.createTable(desc, Bytes.toBytes("A"), Bytes.toBytes("Z"), 5).get(); 110 111 // wait till the table is assigned 112 HMaster master = TEST_UTIL.getHBaseCluster().getMaster(); 113 long timeoutTime = System.currentTimeMillis() + 3000; 114 while (true) { 115 List<RegionInfo> regions = 116 master.getAssignmentManager().getRegionStates().getRegionsOfTable(tableName); 117 if (regions.size() > 3) { 118 return regions.get(2); 119 } 120 long now = System.currentTimeMillis(); 121 if (now > timeoutTime) { 122 fail("Could not find an online region"); 123 } 124 Thread.sleep(10); 125 } 126 } 127 128 @Test 129 public void testGetRegionByStateOfTable() throws Exception { 130 RegionInfo hri = createTableAndGetOneRegion(tableName); 131 132 RegionStates regionStates = 133 TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStates(); 134 assertTrue(regionStates.getRegionByStateOfTable(tableName).get(RegionState.State.OPEN) 135 .stream().anyMatch(r -> RegionInfo.COMPARATOR.compare(r, hri) == 0)); 136 assertFalse(regionStates.getRegionByStateOfTable(TableName.valueOf("I_am_the_phantom")) 137 .get(RegionState.State.OPEN).stream().anyMatch(r -> RegionInfo.COMPARATOR.compare(r, hri) == 0)); 138 } 139 140 @Test 141 public void testMoveRegion() throws Exception { 142 admin.balancerSwitch(false).join(); 143 144 RegionInfo hri = createTableAndGetOneRegion(tableName); 145 RawAsyncHBaseAdmin rawAdmin = (RawAsyncHBaseAdmin) ASYNC_CONN.getAdmin(); 146 ServerName serverName = rawAdmin.getRegionLocation(hri.getRegionName()).get().getServerName(); 147 148 HMaster master = TEST_UTIL.getHBaseCluster().getMaster(); 149 ServerManager serverManager = master.getServerManager(); 150 ServerName destServerName = null; 151 List<JVMClusterUtil.RegionServerThread> regionServers = 152 TEST_UTIL.getHBaseCluster().getLiveRegionServerThreads(); 153 for (JVMClusterUtil.RegionServerThread regionServer : regionServers) { 154 HRegionServer destServer = regionServer.getRegionServer(); 155 destServerName = destServer.getServerName(); 156 if (!destServerName.equals(serverName) && serverManager.isServerOnline(destServerName)) { 157 break; 158 } 159 } 160 161 assertTrue(destServerName != null && !destServerName.equals(serverName)); 162 admin.move(hri.getRegionName(), destServerName).get(); 163 164 long timeoutTime = System.currentTimeMillis() + 30000; 165 while (true) { 166 ServerName sn = rawAdmin.getRegionLocation(hri.getRegionName()).get().getServerName(); 167 if (sn != null && sn.equals(destServerName)) { 168 break; 169 } 170 long now = System.currentTimeMillis(); 171 if (now > timeoutTime) { 172 fail("Failed to move the region in time: " + hri); 173 } 174 Thread.sleep(100); 175 } 176 admin.balancerSwitch(true).join(); 177 } 178 179 @Test 180 public void testGetOnlineRegions() throws Exception { 181 createTableAndGetOneRegion(tableName); 182 AtomicInteger regionServerCount = new AtomicInteger(0); 183 TEST_UTIL 184 .getHBaseCluster() 185 .getLiveRegionServerThreads() 186 .stream() 187 .map(rsThread -> rsThread.getRegionServer()) 188 .forEach( 189 rs -> { 190 ServerName serverName = rs.getServerName(); 191 try { 192 assertEquals(admin.getRegions(serverName).get().size(), rs 193 .getRegions().size()); 194 } catch (Exception e) { 195 fail("admin.getOnlineRegions() method throws a exception: " + e.getMessage()); 196 } 197 regionServerCount.incrementAndGet(); 198 }); 199 assertEquals(2, regionServerCount.get()); 200 } 201 202 @Test 203 public void testFlushTableAndRegion() throws Exception { 204 RegionInfo hri = createTableAndGetOneRegion(tableName); 205 ServerName serverName = 206 TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStates() 207 .getRegionServerOfRegion(hri); 208 HRegionServer regionServer = 209 TEST_UTIL.getHBaseCluster().getLiveRegionServerThreads().stream() 210 .map(rsThread -> rsThread.getRegionServer()) 211 .filter(rs -> rs.getServerName().equals(serverName)).findFirst().get(); 212 213 // write a put into the specific region 214 ASYNC_CONN.getTable(tableName) 215 .put(new Put(hri.getStartKey()).addColumn(FAMILY, FAMILY_0, Bytes.toBytes("value-1"))) 216 .join(); 217 assertTrue(regionServer.getOnlineRegion(hri.getRegionName()).getMemStoreDataSize() > 0); 218 // flush region and wait flush operation finished. 219 LOG.info("flushing region: " + Bytes.toStringBinary(hri.getRegionName())); 220 admin.flushRegion(hri.getRegionName()).get(); 221 LOG.info("blocking until flush is complete: " + Bytes.toStringBinary(hri.getRegionName())); 222 Threads.sleepWithoutInterrupt(500); 223 while (regionServer.getOnlineRegion(hri.getRegionName()).getMemStoreDataSize() > 0) { 224 Threads.sleep(50); 225 } 226 // check the memstore. 227 assertEquals(regionServer.getOnlineRegion(hri.getRegionName()).getMemStoreDataSize(), 0); 228 229 // write another put into the specific region 230 ASYNC_CONN.getTable(tableName) 231 .put(new Put(hri.getStartKey()).addColumn(FAMILY, FAMILY_0, Bytes.toBytes("value-2"))) 232 .join(); 233 assertTrue(regionServer.getOnlineRegion(hri.getRegionName()).getMemStoreDataSize() > 0); 234 admin.flush(tableName).get(); 235 Threads.sleepWithoutInterrupt(500); 236 while (regionServer.getOnlineRegion(hri.getRegionName()).getMemStoreDataSize() > 0) { 237 Threads.sleep(50); 238 } 239 // check the memstore. 240 assertEquals(regionServer.getOnlineRegion(hri.getRegionName()).getMemStoreDataSize(), 0); 241 } 242 243 private void waitUntilMobCompactionFinished(TableName tableName) 244 throws ExecutionException, InterruptedException { 245 long finished = EnvironmentEdgeManager.currentTime() + 60000; 246 CompactionState state = admin.getCompactionState(tableName, CompactType.MOB).get(); 247 while (EnvironmentEdgeManager.currentTime() < finished) { 248 if (state == CompactionState.NONE) { 249 break; 250 } 251 Thread.sleep(10); 252 state = admin.getCompactionState(tableName, CompactType.MOB).get(); 253 } 254 assertEquals(CompactionState.NONE, state); 255 } 256 257 @Test 258 public void testCompactMob() throws Exception { 259 ColumnFamilyDescriptor columnDescriptor = 260 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("mob")) 261 .setMobEnabled(true).setMobThreshold(0).build(); 262 263 TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName) 264 .setColumnFamily(columnDescriptor).build(); 265 266 admin.createTable(tableDescriptor).get(); 267 268 byte[][] families = { Bytes.toBytes("mob") }; 269 loadData(tableName, families, 3000, 8); 270 271 admin.majorCompact(tableName, CompactType.MOB).get(); 272 273 CompactionState state = admin.getCompactionState(tableName, CompactType.MOB).get(); 274 assertNotEquals(CompactionState.NONE, state); 275 276 waitUntilMobCompactionFinished(tableName); 277 } 278 279 @Test 280 public void testCompactRegionServer() throws Exception { 281 byte[][] families = { Bytes.toBytes("f1"), Bytes.toBytes("f2"), Bytes.toBytes("f3") }; 282 createTableWithDefaultConf(tableName, null, families); 283 loadData(tableName, families, 3000, 8); 284 285 List<HRegionServer> rsList = 286 TEST_UTIL.getHBaseCluster().getLiveRegionServerThreads().stream() 287 .map(rsThread -> rsThread.getRegionServer()).collect(Collectors.toList()); 288 List<Region> regions = new ArrayList<>(); 289 rsList.forEach(rs -> regions.addAll(rs.getRegions(tableName))); 290 assertEquals(1, regions.size()); 291 int countBefore = countStoreFilesInFamilies(regions, families); 292 assertTrue(countBefore > 0); 293 294 // Minor compaction for all region servers. 295 for (HRegionServer rs : rsList) 296 admin.compactRegionServer(rs.getServerName()).get(); 297 Thread.sleep(5000); 298 int countAfterMinorCompaction = countStoreFilesInFamilies(regions, families); 299 assertTrue(countAfterMinorCompaction < countBefore); 300 301 // Major compaction for all region servers. 302 for (HRegionServer rs : rsList) 303 admin.majorCompactRegionServer(rs.getServerName()).get(); 304 Thread.sleep(5000); 305 int countAfterMajorCompaction = countStoreFilesInFamilies(regions, families); 306 assertEquals(3, countAfterMajorCompaction); 307 } 308 309 @Test 310 public void testCompact() throws Exception { 311 compactionTest(TableName.valueOf("testCompact1"), 8, CompactionState.MAJOR, false); 312 compactionTest(TableName.valueOf("testCompact2"), 15, CompactionState.MINOR, false); 313 compactionTest(TableName.valueOf("testCompact3"), 8, CompactionState.MAJOR, true); 314 compactionTest(TableName.valueOf("testCompact4"), 15, CompactionState.MINOR, true); 315 } 316 317 private void compactionTest(final TableName tableName, final int flushes, 318 final CompactionState expectedState, boolean singleFamily) throws Exception { 319 // Create a table with regions 320 byte[] family = Bytes.toBytes("family"); 321 byte[][] families = 322 { family, Bytes.add(family, Bytes.toBytes("2")), Bytes.add(family, Bytes.toBytes("3")) }; 323 createTableWithDefaultConf(tableName, null, families); 324 loadData(tableName, families, 3000, flushes); 325 326 List<Region> regions = new ArrayList<>(); 327 TEST_UTIL 328 .getHBaseCluster() 329 .getLiveRegionServerThreads() 330 .forEach(rsThread -> regions.addAll(rsThread.getRegionServer().getRegions(tableName))); 331 assertEquals(1, regions.size()); 332 333 int countBefore = countStoreFilesInFamilies(regions, families); 334 int countBeforeSingleFamily = countStoreFilesInFamily(regions, family); 335 assertTrue(countBefore > 0); // there should be some data files 336 if (expectedState == CompactionState.MINOR) { 337 if (singleFamily) { 338 admin.compact(tableName, family).get(); 339 } else { 340 admin.compact(tableName).get(); 341 } 342 } else { 343 if (singleFamily) { 344 admin.majorCompact(tableName, family).get(); 345 } else { 346 admin.majorCompact(tableName).get(); 347 } 348 } 349 350 long curt = System.currentTimeMillis(); 351 long waitTime = 5000; 352 long endt = curt + waitTime; 353 CompactionState state = admin.getCompactionState(tableName).get(); 354 while (state == CompactionState.NONE && curt < endt) { 355 Thread.sleep(10); 356 state = admin.getCompactionState(tableName).get(); 357 curt = System.currentTimeMillis(); 358 } 359 // Now, should have the right compaction state, 360 // otherwise, the compaction should have already been done 361 if (expectedState != state) { 362 for (Region region : regions) { 363 state = CompactionState.valueOf(region.getCompactionState().toString()); 364 assertEquals(CompactionState.NONE, state); 365 } 366 } else { 367 // Wait until the compaction is done 368 state = admin.getCompactionState(tableName).get(); 369 while (state != CompactionState.NONE && curt < endt) { 370 Thread.sleep(10); 371 state = admin.getCompactionState(tableName).get(); 372 } 373 // Now, compaction should be done. 374 assertEquals(CompactionState.NONE, state); 375 } 376 377 int countAfter = countStoreFilesInFamilies(regions, families); 378 int countAfterSingleFamily = countStoreFilesInFamily(regions, family); 379 assertTrue(countAfter < countBefore); 380 if (!singleFamily) { 381 if (expectedState == CompactionState.MAJOR) assertTrue(families.length == countAfter); 382 else assertTrue(families.length < countAfter); 383 } else { 384 int singleFamDiff = countBeforeSingleFamily - countAfterSingleFamily; 385 // assert only change was to single column family 386 assertTrue(singleFamDiff == (countBefore - countAfter)); 387 if (expectedState == CompactionState.MAJOR) { 388 assertTrue(1 == countAfterSingleFamily); 389 } else { 390 assertTrue(1 < countAfterSingleFamily); 391 } 392 } 393 } 394 395 private static int countStoreFilesInFamily(List<Region> regions, final byte[] family) { 396 return countStoreFilesInFamilies(regions, new byte[][] { family }); 397 } 398 399 private static int countStoreFilesInFamilies(List<Region> regions, final byte[][] families) { 400 int count = 0; 401 for (Region region : regions) { 402 count += region.getStoreFileList(families).size(); 403 } 404 return count; 405 } 406 407 static void loadData(final TableName tableName, final byte[][] families, final int rows) 408 throws IOException { 409 loadData(tableName, families, rows, 1); 410 } 411 412 static void loadData(final TableName tableName, final byte[][] families, final int rows, 413 final int flushes) throws IOException { 414 AsyncTable<?> table = ASYNC_CONN.getTable(tableName); 415 List<Put> puts = new ArrayList<>(rows); 416 byte[] qualifier = Bytes.toBytes("val"); 417 for (int i = 0; i < flushes; i++) { 418 for (int k = 0; k < rows; k++) { 419 byte[] row = Bytes.add(Bytes.toBytes(k), Bytes.toBytes(i)); 420 Put p = new Put(row); 421 for (int j = 0; j < families.length; ++j) { 422 p.addColumn(families[j], qualifier, row); 423 } 424 puts.add(p); 425 } 426 table.putAll(puts).join(); 427 TEST_UTIL.flush(); 428 puts.clear(); 429 } 430 } 431}