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.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory.TRACKER_IMPL; 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertFalse; 024import static org.junit.Assert.assertTrue; 025import static org.junit.Assert.fail; 026 027import java.util.Iterator; 028import java.util.List; 029import java.util.Optional; 030import java.util.concurrent.CompletionException; 031import org.apache.hadoop.hbase.ClientMetaTableAccessor; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.HConstants; 034import org.apache.hadoop.hbase.HRegionLocation; 035import org.apache.hadoop.hbase.TableExistsException; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.TableNotFoundException; 038import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory; 039import org.apache.hadoop.hbase.testclassification.ClientTests; 040import org.apache.hadoop.hbase.testclassification.LargeTests; 041import org.apache.hadoop.hbase.util.Bytes; 042import org.junit.ClassRule; 043import org.junit.Test; 044import org.junit.experimental.categories.Category; 045import org.junit.runner.RunWith; 046import org.junit.runners.Parameterized; 047 048/** 049 * Class to test asynchronous table admin operations. 050 * @see TestAsyncTableAdminApi2 This test and it used to be joined it was taking longer than our ten 051 * minute timeout so they were split. 052 * @see TestAsyncTableAdminApi3 Another split out from this class so each runs under ten minutes. 053 */ 054@RunWith(Parameterized.class) 055@Category({ LargeTests.class, ClientTests.class }) 056public class TestAsyncTableAdminApi extends TestAsyncAdminBase { 057 058 @ClassRule 059 public static final HBaseClassTestRule CLASS_RULE = 060 HBaseClassTestRule.forClass(TestAsyncTableAdminApi.class); 061 062 @Test 063 public void testCreateTable() throws Exception { 064 List<TableDescriptor> tables = admin.listTableDescriptors().get(); 065 int numTables = tables.size(); 066 createTableWithDefaultConf(tableName); 067 tables = admin.listTableDescriptors().get(); 068 assertEquals(numTables + 1, tables.size()); 069 assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster().getMaster() 070 .getTableStateManager().isTableState(tableName, TableState.State.ENABLED)); 071 assertEquals(TableState.State.ENABLED, getStateFromMeta(tableName)); 072 } 073 074 static TableState.State getStateFromMeta(TableName table) throws Exception { 075 Optional<TableState> state = ClientMetaTableAccessor 076 .getTableState(ASYNC_CONN.getTable(TableName.META_TABLE_NAME), table).get(); 077 assertTrue(state.isPresent()); 078 return state.get().getState(); 079 } 080 081 @Test 082 public void testCreateTableNumberOfRegions() throws Exception { 083 AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME); 084 085 createTableWithDefaultConf(tableName); 086 List<HRegionLocation> regionLocations = 087 ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 088 assertEquals("Table should have only 1 region", 1, regionLocations.size()); 089 090 final TableName tableName2 = TableName.valueOf(tableName.getNameAsString() + "_2"); 091 createTableWithDefaultConf(tableName2, new byte[][] { new byte[] { 42 } }); 092 regionLocations = ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName2).get(); 093 assertEquals("Table should have only 2 region", 2, regionLocations.size()); 094 095 final TableName tableName3 = TableName.valueOf(tableName.getNameAsString() + "_3"); 096 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName3); 097 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)); 098 admin.createTable(builder.build(), Bytes.toBytes("a"), Bytes.toBytes("z"), 3).join(); 099 regionLocations = ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName3).get(); 100 assertEquals("Table should have only 3 region", 3, regionLocations.size()); 101 102 final TableName tableName4 = TableName.valueOf(tableName.getNameAsString() + "_4"); 103 builder = TableDescriptorBuilder.newBuilder(tableName4); 104 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)); 105 try { 106 admin.createTable(builder.build(), Bytes.toBytes("a"), Bytes.toBytes("z"), 2).join(); 107 fail("Should not be able to create a table with only 2 regions using this API."); 108 } catch (CompletionException e) { 109 assertTrue(e.getCause() instanceof IllegalArgumentException); 110 } 111 112 final TableName tableName5 = TableName.valueOf(tableName.getNameAsString() + "_5"); 113 builder = TableDescriptorBuilder.newBuilder(tableName5); 114 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)); 115 admin.createTable(builder.build(), new byte[] { 1 }, new byte[] { 127 }, 16).join(); 116 regionLocations = ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName5).get(); 117 assertEquals("Table should have 16 region", 16, regionLocations.size()); 118 } 119 120 @Test 121 public void testCreateTableWithRegions() throws Exception { 122 byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 }, new byte[] { 3, 3, 3 }, 123 new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 }, new byte[] { 6, 6, 6 }, 124 new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 }, new byte[] { 9, 9, 9 }, }; 125 int expectedRegions = splitKeys.length + 1; 126 createTableWithDefaultConf(tableName, splitKeys); 127 128 boolean tableAvailable = admin.isTableAvailable(tableName).get(); 129 assertTrue("Table should be created with splitKyes + 1 rows in META", tableAvailable); 130 131 AsyncTable<AdvancedScanResultConsumer> metaTable = ASYNC_CONN.getTable(META_TABLE_NAME); 132 List<HRegionLocation> regions = 133 ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName).get(); 134 Iterator<HRegionLocation> hris = regions.iterator(); 135 136 assertEquals( 137 "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(), 138 expectedRegions, regions.size()); 139 System.err.println("Found " + regions.size() + " regions"); 140 141 RegionInfo hri; 142 hris = regions.iterator(); 143 hri = hris.next().getRegion(); 144 assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0); 145 assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0])); 146 hri = hris.next().getRegion(); 147 assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0])); 148 assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1])); 149 hri = hris.next().getRegion(); 150 assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1])); 151 assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2])); 152 hri = hris.next().getRegion(); 153 assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2])); 154 assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3])); 155 hri = hris.next().getRegion(); 156 assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3])); 157 assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4])); 158 hri = hris.next().getRegion(); 159 assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4])); 160 assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5])); 161 hri = hris.next().getRegion(); 162 assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5])); 163 assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6])); 164 hri = hris.next().getRegion(); 165 assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6])); 166 assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7])); 167 hri = hris.next().getRegion(); 168 assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7])); 169 assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8])); 170 hri = hris.next().getRegion(); 171 assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8])); 172 assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0); 173 174 // Now test using start/end with a number of regions 175 176 // Use 80 bit numbers to make sure we aren't limited 177 byte[] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; 178 byte[] endKey = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; 179 180 // Splitting into 10 regions, we expect (null,1) ... (9, null) 181 // with (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8) (8,9) in the middle 182 expectedRegions = 10; 183 final TableName tableName2 = TableName.valueOf(tableName.getNameAsString() + "_2"); 184 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName2); 185 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)); 186 admin.createTable(builder.build(), startKey, endKey, expectedRegions).join(); 187 188 regions = ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName2).get(); 189 assertEquals( 190 "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(), 191 expectedRegions, regions.size()); 192 System.err.println("Found " + regions.size() + " regions"); 193 194 hris = regions.iterator(); 195 hri = hris.next().getRegion(); 196 assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0); 197 assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 })); 198 hri = hris.next().getRegion(); 199 assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 })); 200 assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 })); 201 hri = hris.next().getRegion(); 202 assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 })); 203 assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 })); 204 hri = hris.next().getRegion(); 205 assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 })); 206 assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 })); 207 hri = hris.next().getRegion(); 208 assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 })); 209 assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 })); 210 hri = hris.next().getRegion(); 211 assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 })); 212 assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 })); 213 hri = hris.next().getRegion(); 214 assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 })); 215 assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 })); 216 hri = hris.next().getRegion(); 217 assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 })); 218 assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 })); 219 hri = hris.next().getRegion(); 220 assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 })); 221 assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 })); 222 hri = hris.next().getRegion(); 223 assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 })); 224 assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0); 225 226 // Try once more with something that divides into something infinite 227 startKey = new byte[] { 0, 0, 0, 0, 0, 0 }; 228 endKey = new byte[] { 1, 0, 0, 0, 0, 0 }; 229 230 expectedRegions = 5; 231 final TableName tableName3 = TableName.valueOf(tableName.getNameAsString() + "_3"); 232 builder = TableDescriptorBuilder.newBuilder(tableName3); 233 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY)); 234 admin.createTable(builder.build(), startKey, endKey, expectedRegions).join(); 235 236 regions = ClientMetaTableAccessor.getTableHRegionLocations(metaTable, tableName3).get(); 237 assertEquals( 238 "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(), 239 expectedRegions, regions.size()); 240 System.err.println("Found " + regions.size() + " regions"); 241 242 // Try an invalid case where there are duplicate split keys 243 splitKeys = new byte[][] { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 }, 244 new byte[] { 3, 3, 3 }, new byte[] { 2, 2, 2 } }; 245 final TableName tableName4 = TableName.valueOf(tableName.getNameAsString() + "_4"); 246 try { 247 createTableWithDefaultConf(tableName4, splitKeys); 248 fail("Should not be able to create this table because of " + "duplicate split keys"); 249 } catch (CompletionException e) { 250 assertTrue(e.getCause() instanceof IllegalArgumentException); 251 } 252 } 253 254 @Test 255 public void testCreateTableWithOnlyEmptyStartRow() throws Exception { 256 byte[][] splitKeys = new byte[1][]; 257 splitKeys[0] = HConstants.EMPTY_BYTE_ARRAY; 258 try { 259 createTableWithDefaultConf(tableName, splitKeys); 260 fail("Test case should fail as empty split key is passed."); 261 } catch (CompletionException e) { 262 assertTrue(e.getCause() instanceof IllegalArgumentException); 263 } 264 } 265 266 @Test 267 public void testCreateTableWithEmptyRowInTheSplitKeys() throws Exception { 268 byte[][] splitKeys = new byte[3][]; 269 splitKeys[0] = Bytes.toBytes("region1"); 270 splitKeys[1] = HConstants.EMPTY_BYTE_ARRAY; 271 splitKeys[2] = Bytes.toBytes("region2"); 272 try { 273 createTableWithDefaultConf(tableName, splitKeys); 274 fail("Test case should fail as empty split key is passed."); 275 } catch (CompletionException e) { 276 assertTrue(e.getCause() instanceof IllegalArgumentException); 277 } 278 } 279 280 @Test 281 public void testDeleteTable() throws Exception { 282 createTableWithDefaultConf(tableName); 283 assertTrue(admin.tableExists(tableName).get()); 284 TEST_UTIL.getAdmin().disableTable(tableName); 285 admin.deleteTable(tableName).join(); 286 assertFalse(admin.tableExists(tableName).get()); 287 } 288 289 @Test 290 public void testTruncateTable() throws Exception { 291 testTruncateTable(tableName, false); 292 } 293 294 @Test 295 public void testTruncateTablePreservingSplits() throws Exception { 296 testTruncateTable(tableName, true); 297 } 298 299 private void testTruncateTable(final TableName tableName, boolean preserveSplits) 300 throws Exception { 301 byte[][] splitKeys = new byte[2][]; 302 splitKeys[0] = Bytes.toBytes(4); 303 splitKeys[1] = Bytes.toBytes(8); 304 305 // Create & Fill the table 306 createTableWithDefaultConf(tableName, splitKeys); 307 AsyncTable<?> table = ASYNC_CONN.getTable(tableName); 308 int expectedRows = 10; 309 for (int i = 0; i < expectedRows; i++) { 310 byte[] data = Bytes.toBytes(String.valueOf(i)); 311 Put put = new Put(data); 312 put.addColumn(FAMILY, null, data); 313 table.put(put).join(); 314 } 315 assertEquals(10, table.scanAll(new Scan()).get().size()); 316 assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size()); 317 318 // Truncate & Verify 319 admin.disableTable(tableName).join(); 320 admin.truncateTable(tableName, preserveSplits).join(); 321 assertEquals(0, table.scanAll(new Scan()).get().size()); 322 if (preserveSplits) { 323 assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size()); 324 } else { 325 assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(tableName).size()); 326 } 327 } 328 329 @Test 330 public void testCloneTableSchema() throws Exception { 331 final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new"); 332 testCloneTableSchema(tableName, newTableName, false); 333 } 334 335 @Test 336 public void testCloneTableSchemaPreservingSplits() throws Exception { 337 final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new"); 338 testCloneTableSchema(tableName, newTableName, true); 339 } 340 341 private void testCloneTableSchema(final TableName tableName, final TableName newTableName, 342 boolean preserveSplits) throws Exception { 343 byte[][] splitKeys = new byte[2][]; 344 splitKeys[0] = Bytes.toBytes(4); 345 splitKeys[1] = Bytes.toBytes(8); 346 int NUM_FAMILYS = 2; 347 int NUM_REGIONS = 3; 348 int BLOCK_SIZE = 1024; 349 int TTL = 86400; 350 boolean BLOCK_CACHE = false; 351 352 // Create the table 353 TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(tableName) 354 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_0)) 355 .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(FAMILY_1).setBlocksize(BLOCK_SIZE) 356 .setBlockCacheEnabled(BLOCK_CACHE).setTimeToLive(TTL).build()) 357 .build(); 358 admin.createTable(tableDesc, splitKeys).join(); 359 360 assertEquals(NUM_REGIONS, TEST_UTIL.getHBaseCluster().getRegions(tableName).size()); 361 assertTrue("Table should be created with splitKyes + 1 rows in META", 362 admin.isTableAvailable(tableName).get()); 363 364 // Clone & Verify 365 admin.cloneTableSchema(tableName, newTableName, preserveSplits).join(); 366 TableDescriptor newTableDesc = admin.getDescriptor(newTableName).get(); 367 368 assertEquals(NUM_FAMILYS, newTableDesc.getColumnFamilyCount()); 369 assertEquals(BLOCK_SIZE, newTableDesc.getColumnFamily(FAMILY_1).getBlocksize()); 370 assertEquals(BLOCK_CACHE, newTableDesc.getColumnFamily(FAMILY_1).isBlockCacheEnabled()); 371 assertEquals(TTL, newTableDesc.getColumnFamily(FAMILY_1).getTimeToLive()); 372 // HBASE-26246 introduced persist of store file tracker into table descriptor 373 tableDesc = TableDescriptorBuilder.newBuilder(tableDesc).setValue(TRACKER_IMPL, 374 StoreFileTrackerFactory.getStoreFileTrackerName(TEST_UTIL.getConfiguration())).build(); 375 TEST_UTIL.verifyTableDescriptorIgnoreTableName(tableDesc, newTableDesc); 376 377 if (preserveSplits) { 378 assertEquals(NUM_REGIONS, TEST_UTIL.getHBaseCluster().getRegions(newTableName).size()); 379 assertTrue("New table should be created with splitKyes + 1 rows in META", 380 admin.isTableAvailable(newTableName).get()); 381 } else { 382 assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(newTableName).size()); 383 } 384 } 385 386 @Test 387 public void testCloneTableSchemaWithNonExistentSourceTable() throws Exception { 388 final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new"); 389 // test for non-existent source table 390 try { 391 admin.cloneTableSchema(tableName, newTableName, false).join(); 392 fail("Should have failed when source table doesn't exist."); 393 } catch (CompletionException e) { 394 assertTrue(e.getCause() instanceof TableNotFoundException); 395 } 396 } 397 398 @Test 399 public void testCloneTableSchemaWithExistentDestinationTable() throws Exception { 400 final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new"); 401 byte[] FAMILY_0 = Bytes.toBytes("cf0"); 402 TEST_UTIL.createTable(tableName, FAMILY_0); 403 TEST_UTIL.createTable(newTableName, FAMILY_0); 404 // test for existent destination table 405 try { 406 admin.cloneTableSchema(tableName, newTableName, false).join(); 407 fail("Should have failed when destination table exists."); 408 } catch (CompletionException e) { 409 assertTrue(e.getCause() instanceof TableExistsException); 410 } 411 } 412 413 @Test 414 public void testIsTableAvailableWithInexistantTable() throws Exception { 415 final TableName newTableName = TableName.valueOf(tableName.getNameAsString() + "_new"); 416 // test for inexistant table 417 assertFalse(admin.isTableAvailable(newTableName).get()); 418 } 419}