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.namespace; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotNull; 023import static org.junit.Assert.assertNull; 024import static org.junit.Assert.assertTrue; 025import static org.junit.Assert.fail; 026 027import java.io.IOException; 028import java.util.Collections; 029import java.util.List; 030import java.util.Optional; 031import java.util.concurrent.CountDownLatch; 032import java.util.concurrent.ExecutionException; 033import java.util.concurrent.Future; 034import java.util.concurrent.TimeUnit; 035import org.apache.hadoop.conf.Configuration; 036import org.apache.hadoop.fs.FileSystem; 037import org.apache.hadoop.fs.Path; 038import org.apache.hadoop.hbase.Coprocessor; 039import org.apache.hadoop.hbase.CoprocessorEnvironment; 040import org.apache.hadoop.hbase.HBaseClassTestRule; 041import org.apache.hadoop.hbase.HBaseTestingUtility; 042import org.apache.hadoop.hbase.HColumnDescriptor; 043import org.apache.hadoop.hbase.HConstants; 044import org.apache.hadoop.hbase.HRegionInfo; 045import org.apache.hadoop.hbase.HTableDescriptor; 046import org.apache.hadoop.hbase.MiniHBaseCluster; 047import org.apache.hadoop.hbase.NamespaceDescriptor; 048import org.apache.hadoop.hbase.StartMiniClusterOption; 049import org.apache.hadoop.hbase.TableName; 050import org.apache.hadoop.hbase.Waiter; 051import org.apache.hadoop.hbase.client.Admin; 052import org.apache.hadoop.hbase.client.CompactionState; 053import org.apache.hadoop.hbase.client.Connection; 054import org.apache.hadoop.hbase.client.ConnectionFactory; 055import org.apache.hadoop.hbase.client.DoNotRetryRegionException; 056import org.apache.hadoop.hbase.client.RegionInfo; 057import org.apache.hadoop.hbase.client.RegionLocator; 058import org.apache.hadoop.hbase.client.Table; 059import org.apache.hadoop.hbase.client.TableDescriptor; 060import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 061import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 062import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 063import org.apache.hadoop.hbase.coprocessor.MasterObserver; 064import org.apache.hadoop.hbase.coprocessor.ObserverContext; 065import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor; 066import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 067import org.apache.hadoop.hbase.coprocessor.RegionObserver; 068import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessor; 069import org.apache.hadoop.hbase.coprocessor.RegionServerObserver; 070import org.apache.hadoop.hbase.master.HMaster; 071import org.apache.hadoop.hbase.master.MasterCoprocessorHost; 072import org.apache.hadoop.hbase.master.TableNamespaceManager; 073import org.apache.hadoop.hbase.quotas.MasterQuotaManager; 074import org.apache.hadoop.hbase.quotas.QuotaExceededException; 075import org.apache.hadoop.hbase.quotas.QuotaUtil; 076import org.apache.hadoop.hbase.regionserver.HRegion; 077import org.apache.hadoop.hbase.regionserver.Store; 078import org.apache.hadoop.hbase.regionserver.StoreFile; 079import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker; 080import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest; 081import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException; 082import org.apache.hadoop.hbase.testclassification.LargeTests; 083import org.apache.hadoop.hbase.util.Bytes; 084import org.apache.hadoop.hbase.util.CommonFSUtils; 085import org.apache.zookeeper.KeeperException; 086import org.junit.After; 087import org.junit.AfterClass; 088import org.junit.BeforeClass; 089import org.junit.ClassRule; 090import org.junit.Test; 091import org.junit.experimental.categories.Category; 092import org.slf4j.Logger; 093import org.slf4j.LoggerFactory; 094 095@Category(LargeTests.class) 096public class TestNamespaceAuditor { 097 098 @ClassRule 099 public static final HBaseClassTestRule CLASS_RULE = 100 HBaseClassTestRule.forClass(TestNamespaceAuditor.class); 101 102 private static final Logger LOG = LoggerFactory.getLogger(TestNamespaceAuditor.class); 103 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 104 private static Admin ADMIN; 105 private String prefix = "TestNamespaceAuditor"; 106 107 @BeforeClass 108 public static void before() throws Exception { 109 Configuration conf = UTIL.getConfiguration(); 110 conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, CustomObserver.class.getName()); 111 conf.setStrings(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, MasterSyncObserver.class.getName(), 112 CPMasterObserver.class.getName()); 113 conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 5); 114 conf.setBoolean(QuotaUtil.QUOTA_CONF_KEY, true); 115 conf.setClass("hbase.coprocessor.regionserver.classes", CPRegionServerObserver.class, 116 RegionServerObserver.class); 117 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(2).build(); 118 UTIL.startMiniCluster(option); 119 waitForQuotaInitialize(UTIL); 120 ADMIN = UTIL.getAdmin(); 121 } 122 123 @AfterClass 124 public static void tearDown() throws Exception { 125 UTIL.shutdownMiniCluster(); 126 } 127 128 @After 129 public void cleanup() throws Exception, KeeperException { 130 for (HTableDescriptor table : ADMIN.listTables()) { 131 ADMIN.disableTable(table.getTableName()); 132 deleteTable(table.getTableName()); 133 } 134 for (NamespaceDescriptor ns : ADMIN.listNamespaceDescriptors()) { 135 if (ns.getName().startsWith(prefix)) { 136 ADMIN.deleteNamespace(ns.getName()); 137 } 138 } 139 assertTrue("Quota manager not initialized", 140 UTIL.getHBaseCluster().getMaster().getMasterQuotaManager().isQuotaInitialized()); 141 } 142 143 @Test 144 public void testTableOperations() throws Exception { 145 String nsp = prefix + "_np2"; 146 NamespaceDescriptor nspDesc = 147 NamespaceDescriptor.create(nsp).addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "5") 148 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build(); 149 ADMIN.createNamespace(nspDesc); 150 assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(nsp)); 151 assertEquals(3, ADMIN.listNamespaceDescriptors().length); 152 HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); 153 154 HTableDescriptor tableDescOne = 155 new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1")); 156 tableDescOne.addFamily(fam1); 157 HTableDescriptor tableDescTwo = 158 new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2")); 159 tableDescTwo.addFamily(fam1); 160 HTableDescriptor tableDescThree = 161 new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table3")); 162 tableDescThree.addFamily(fam1); 163 ADMIN.createTable(tableDescOne); 164 boolean constraintViolated = false; 165 try { 166 ADMIN.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 5); 167 } catch (Exception exp) { 168 assertTrue(exp instanceof IOException); 169 constraintViolated = true; 170 } finally { 171 assertTrue("Constraint not violated for table " + tableDescTwo.getTableName(), 172 constraintViolated); 173 } 174 ADMIN.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4); 175 NamespaceTableAndRegionInfo nspState = getQuotaManager().getState(nsp); 176 assertNotNull(nspState); 177 assertTrue(nspState.getTables().size() == 2); 178 assertTrue(nspState.getRegionCount() == 5); 179 constraintViolated = false; 180 try { 181 ADMIN.createTable(tableDescThree); 182 } catch (Exception exp) { 183 assertTrue(exp instanceof IOException); 184 constraintViolated = true; 185 } finally { 186 assertTrue("Constraint not violated for table " + tableDescThree.getTableName(), 187 constraintViolated); 188 } 189 } 190 191 @Test 192 public void testValidQuotas() throws Exception { 193 boolean exceptionCaught = false; 194 FileSystem fs = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getFileSystem(); 195 Path rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir(); 196 NamespaceDescriptor nspDesc = NamespaceDescriptor.create(prefix + "vq1") 197 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "hihdufh") 198 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build(); 199 try { 200 ADMIN.createNamespace(nspDesc); 201 } catch (Exception exp) { 202 LOG.warn(exp.toString(), exp); 203 exceptionCaught = true; 204 } finally { 205 assertTrue(exceptionCaught); 206 assertFalse(fs.exists(CommonFSUtils.getNamespaceDir(rootDir, nspDesc.getName()))); 207 } 208 nspDesc = NamespaceDescriptor.create(prefix + "vq2") 209 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "-456") 210 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build(); 211 try { 212 ADMIN.createNamespace(nspDesc); 213 } catch (Exception exp) { 214 LOG.warn(exp.toString(), exp); 215 exceptionCaught = true; 216 } finally { 217 assertTrue(exceptionCaught); 218 assertFalse(fs.exists(CommonFSUtils.getNamespaceDir(rootDir, nspDesc.getName()))); 219 } 220 nspDesc = NamespaceDescriptor.create(prefix + "vq3") 221 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "10") 222 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "sciigd").build(); 223 try { 224 ADMIN.createNamespace(nspDesc); 225 } catch (Exception exp) { 226 LOG.warn(exp.toString(), exp); 227 exceptionCaught = true; 228 } finally { 229 assertTrue(exceptionCaught); 230 assertFalse(fs.exists(CommonFSUtils.getNamespaceDir(rootDir, nspDesc.getName()))); 231 } 232 nspDesc = NamespaceDescriptor.create(prefix + "vq4") 233 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "10") 234 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "-1500").build(); 235 try { 236 ADMIN.createNamespace(nspDesc); 237 } catch (Exception exp) { 238 LOG.warn(exp.toString(), exp); 239 exceptionCaught = true; 240 } finally { 241 assertTrue(exceptionCaught); 242 assertFalse(fs.exists(CommonFSUtils.getNamespaceDir(rootDir, nspDesc.getName()))); 243 } 244 } 245 246 @Test 247 public void testDeleteTable() throws Exception { 248 String namespace = prefix + "_dummy"; 249 NamespaceDescriptor nspDesc = NamespaceDescriptor.create(namespace) 250 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "100") 251 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "3").build(); 252 ADMIN.createNamespace(nspDesc); 253 assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(namespace)); 254 NamespaceTableAndRegionInfo stateInfo = getNamespaceState(nspDesc.getName()); 255 assertNotNull("Namespace state found null for " + namespace, stateInfo); 256 HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); 257 HTableDescriptor tableDescOne = 258 new HTableDescriptor(TableName.valueOf(namespace + TableName.NAMESPACE_DELIM + "table1")); 259 tableDescOne.addFamily(fam1); 260 HTableDescriptor tableDescTwo = 261 new HTableDescriptor(TableName.valueOf(namespace + TableName.NAMESPACE_DELIM + "table2")); 262 tableDescTwo.addFamily(fam1); 263 ADMIN.createTable(tableDescOne); 264 ADMIN.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 5); 265 stateInfo = getNamespaceState(nspDesc.getName()); 266 assertNotNull("Namespace state found to be null.", stateInfo); 267 assertEquals(2, stateInfo.getTables().size()); 268 assertEquals(5, stateInfo.getRegionCountOfTable(tableDescTwo.getTableName())); 269 assertEquals(6, stateInfo.getRegionCount()); 270 ADMIN.disableTable(tableDescOne.getTableName()); 271 deleteTable(tableDescOne.getTableName()); 272 stateInfo = getNamespaceState(nspDesc.getName()); 273 assertNotNull("Namespace state found to be null.", stateInfo); 274 assertEquals(5, stateInfo.getRegionCount()); 275 assertEquals(1, stateInfo.getTables().size()); 276 ADMIN.disableTable(tableDescTwo.getTableName()); 277 deleteTable(tableDescTwo.getTableName()); 278 ADMIN.deleteNamespace(namespace); 279 stateInfo = getNamespaceState(namespace); 280 assertNull("Namespace state not found to be null.", stateInfo); 281 } 282 283 public static class CPRegionServerObserver 284 implements RegionServerCoprocessor, RegionServerObserver { 285 private volatile boolean shouldFailMerge = false; 286 287 public void failMerge(boolean fail) { 288 shouldFailMerge = fail; 289 } 290 291 private boolean triggered = false; 292 293 public synchronized void waitUtilTriggered() throws InterruptedException { 294 while (!triggered) { 295 wait(); 296 } 297 } 298 299 @Override 300 public Optional<RegionServerObserver> getRegionServerObserver() { 301 return Optional.of(this); 302 } 303 } 304 305 public static class CPMasterObserver implements MasterCoprocessor, MasterObserver { 306 private volatile boolean shouldFailMerge = false; 307 308 public void failMerge(boolean fail) { 309 shouldFailMerge = fail; 310 } 311 312 @Override 313 public Optional<MasterObserver> getMasterObserver() { 314 return Optional.of(this); 315 } 316 317 @Override 318 public synchronized void preMergeRegionsAction( 319 final ObserverContext<MasterCoprocessorEnvironment> ctx, final RegionInfo[] regionsToMerge) 320 throws IOException { 321 notifyAll(); 322 if (shouldFailMerge) { 323 throw new IOException("fail merge"); 324 } 325 } 326 } 327 328 @Test 329 public void testRegionMerge() throws Exception { 330 String nsp1 = prefix + "_regiontest"; 331 final int initialRegions = 3; 332 NamespaceDescriptor nspDesc = NamespaceDescriptor.create(nsp1) 333 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "" + initialRegions) 334 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build(); 335 ADMIN.createNamespace(nspDesc); 336 final TableName tableTwo = TableName.valueOf(nsp1 + TableName.NAMESPACE_DELIM + "table2"); 337 byte[] columnFamily = Bytes.toBytes("info"); 338 HTableDescriptor tableDescOne = new HTableDescriptor(tableTwo); 339 tableDescOne.addFamily(new HColumnDescriptor(columnFamily)); 340 ADMIN.createTable(tableDescOne, Bytes.toBytes("0"), Bytes.toBytes("9"), initialRegions); 341 Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration()); 342 try (Table table = connection.getTable(tableTwo)) { 343 UTIL.loadNumericRows(table, Bytes.toBytes("info"), 1000, 1999); 344 } 345 ADMIN.flush(tableTwo); 346 List<RegionInfo> hris = ADMIN.getRegions(tableTwo); 347 assertEquals(initialRegions, hris.size()); 348 Collections.sort(hris, RegionInfo.COMPARATOR); 349 Future<?> f = ADMIN.mergeRegionsAsync(hris.get(0).getEncodedNameAsBytes(), 350 hris.get(1).getEncodedNameAsBytes(), false); 351 f.get(10, TimeUnit.SECONDS); 352 353 hris = ADMIN.getRegions(tableTwo); 354 assertEquals(initialRegions - 1, hris.size()); 355 Collections.sort(hris, RegionInfo.COMPARATOR); 356 byte[] splitKey = Bytes.toBytes("3"); 357 HRegion regionToSplit = UTIL.getMiniHBaseCluster().getRegions(tableTwo).stream() 358 .filter(r -> r.getRegionInfo().containsRow(splitKey)).findFirst().get(); 359 regionToSplit.compact(true); 360 // Waiting for compaction to finish 361 UTIL.waitFor(30000, new Waiter.Predicate<Exception>() { 362 @Override 363 public boolean evaluate() throws Exception { 364 return (CompactionState.NONE 365 == ADMIN.getCompactionStateForRegion(regionToSplit.getRegionInfo().getRegionName())); 366 } 367 }); 368 369 // Cleaning compacted references for split to proceed 370 regionToSplit.getStores().stream().forEach(s -> { 371 try { 372 s.closeAndArchiveCompactedFiles(); 373 } catch (IOException e1) { 374 LOG.error("Error whiling cleaning compacted file"); 375 } 376 }); 377 // the above compact may quit immediately if there is a compaction ongoing, so here we need to 378 // wait a while to let the ongoing compaction finish. 379 UTIL.waitFor(10000, regionToSplit::isSplittable); 380 ADMIN.splitRegionAsync(regionToSplit.getRegionInfo().getRegionName(), splitKey).get(10, 381 TimeUnit.SECONDS); 382 hris = ADMIN.getRegions(tableTwo); 383 assertEquals(initialRegions, hris.size()); 384 Collections.sort(hris, RegionInfo.COMPARATOR); 385 386 // Fail region merge through Coprocessor hook 387 MiniHBaseCluster cluster = UTIL.getHBaseCluster(); 388 MasterCoprocessorHost cpHost = cluster.getMaster().getMasterCoprocessorHost(); 389 Coprocessor coprocessor = cpHost.findCoprocessor(CPMasterObserver.class); 390 CPMasterObserver masterObserver = (CPMasterObserver) coprocessor; 391 masterObserver.failMerge(true); 392 393 f = ADMIN.mergeRegionsAsync(hris.get(1).getEncodedNameAsBytes(), 394 hris.get(2).getEncodedNameAsBytes(), false); 395 try { 396 f.get(10, TimeUnit.SECONDS); 397 fail("Merge was supposed to fail!"); 398 } catch (ExecutionException ee) { 399 // Expected. 400 } 401 hris = ADMIN.getRegions(tableTwo); 402 assertEquals(initialRegions, hris.size()); 403 Collections.sort(hris, RegionInfo.COMPARATOR); 404 // verify that we cannot split 405 try { 406 ADMIN.split(tableTwo, Bytes.toBytes("6")); 407 fail(); 408 } catch (DoNotRetryRegionException e) { 409 // Expected 410 } 411 Thread.sleep(2000); 412 assertEquals(initialRegions, ADMIN.getRegions(tableTwo).size()); 413 } 414 415 /* 416 * Create a table and make sure that the table creation fails after adding this table entry into 417 * namespace quota cache. Now correct the failure and recreate the table with same name. 418 * HBASE-13394 419 */ 420 @Test 421 public void testRecreateTableWithSameNameAfterFirstTimeFailure() throws Exception { 422 String nsp1 = prefix + "_testRecreateTable"; 423 NamespaceDescriptor nspDesc = 424 NamespaceDescriptor.create(nsp1).addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "20") 425 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "1").build(); 426 ADMIN.createNamespace(nspDesc); 427 final TableName tableOne = TableName.valueOf(nsp1 + TableName.NAMESPACE_DELIM + "table1"); 428 byte[] columnFamily = Bytes.toBytes("info"); 429 HTableDescriptor tableDescOne = new HTableDescriptor(tableOne); 430 tableDescOne.addFamily(new HColumnDescriptor(columnFamily)); 431 MasterSyncObserver.throwExceptionInPreCreateTableAction = true; 432 try { 433 try { 434 ADMIN.createTable(tableDescOne); 435 fail("Table " + tableOne.toString() + "creation should fail."); 436 } catch (Exception exp) { 437 LOG.error(exp.toString(), exp); 438 } 439 assertFalse(ADMIN.tableExists(tableOne)); 440 441 NamespaceTableAndRegionInfo nstate = getNamespaceState(nsp1); 442 assertEquals("First table creation failed in namespace so number of tables in namespace " 443 + "should be 0.", 0, nstate.getTables().size()); 444 445 MasterSyncObserver.throwExceptionInPreCreateTableAction = false; 446 try { 447 ADMIN.createTable(tableDescOne); 448 } catch (Exception e) { 449 fail("Table " + tableOne.toString() + "creation should succeed."); 450 LOG.error(e.toString(), e); 451 } 452 assertTrue(ADMIN.tableExists(tableOne)); 453 nstate = getNamespaceState(nsp1); 454 assertEquals( 455 "First table was created successfully so table size in namespace should " + "be one now.", 456 1, nstate.getTables().size()); 457 } finally { 458 MasterSyncObserver.throwExceptionInPreCreateTableAction = false; 459 if (ADMIN.tableExists(tableOne)) { 460 ADMIN.disableTable(tableOne); 461 deleteTable(tableOne); 462 } 463 ADMIN.deleteNamespace(nsp1); 464 } 465 } 466 467 private NamespaceTableAndRegionInfo getNamespaceState(String namespace) 468 throws KeeperException, IOException { 469 return getQuotaManager().getState(namespace); 470 } 471 472 public static class CustomObserver implements RegionCoprocessor, RegionObserver { 473 volatile CountDownLatch postCompact; 474 475 @Override 476 public void postCompact(ObserverContext<RegionCoprocessorEnvironment> e, Store store, 477 StoreFile resultFile, CompactionLifeCycleTracker tracker, CompactionRequest request) 478 throws IOException { 479 postCompact.countDown(); 480 } 481 482 @Override 483 public void start(CoprocessorEnvironment e) throws IOException { 484 postCompact = new CountDownLatch(1); 485 } 486 487 @Override 488 public Optional<RegionObserver> getRegionObserver() { 489 return Optional.of(this); 490 } 491 } 492 493 @Test 494 public void testStatePreserve() throws Exception { 495 final String nsp1 = prefix + "_testStatePreserve"; 496 NamespaceDescriptor nspDesc = 497 NamespaceDescriptor.create(nsp1).addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "20") 498 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "10").build(); 499 ADMIN.createNamespace(nspDesc); 500 TableName tableOne = TableName.valueOf(nsp1 + TableName.NAMESPACE_DELIM + "table1"); 501 TableName tableTwo = TableName.valueOf(nsp1 + TableName.NAMESPACE_DELIM + "table2"); 502 TableName tableThree = TableName.valueOf(nsp1 + TableName.NAMESPACE_DELIM + "table3"); 503 HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); 504 HTableDescriptor tableDescOne = new HTableDescriptor(tableOne); 505 tableDescOne.addFamily(fam1); 506 HTableDescriptor tableDescTwo = new HTableDescriptor(tableTwo); 507 tableDescTwo.addFamily(fam1); 508 HTableDescriptor tableDescThree = new HTableDescriptor(tableThree); 509 tableDescThree.addFamily(fam1); 510 ADMIN.createTable(tableDescOne, Bytes.toBytes("1"), Bytes.toBytes("1000"), 3); 511 ADMIN.createTable(tableDescTwo, Bytes.toBytes("1"), Bytes.toBytes("1000"), 3); 512 ADMIN.createTable(tableDescThree, Bytes.toBytes("1"), Bytes.toBytes("1000"), 4); 513 ADMIN.disableTable(tableThree); 514 deleteTable(tableThree); 515 // wait for chore to complete 516 UTIL.waitFor(1000, new Waiter.Predicate<Exception>() { 517 @Override 518 public boolean evaluate() throws Exception { 519 return (getNamespaceState(nsp1).getTables().size() == 2); 520 } 521 }); 522 NamespaceTableAndRegionInfo before = getNamespaceState(nsp1); 523 killActiveMaster(); 524 NamespaceTableAndRegionInfo after = getNamespaceState(nsp1); 525 assertEquals("Expected: " + before.getTables() + " Found: " + after.getTables(), 526 before.getTables().size(), after.getTables().size()); 527 } 528 529 public static void waitForQuotaInitialize(final HBaseTestingUtility util) throws Exception { 530 util.waitFor(60000, new Waiter.Predicate<Exception>() { 531 @Override 532 public boolean evaluate() throws Exception { 533 HMaster master = util.getHBaseCluster().getMaster(); 534 if (master == null) { 535 return false; 536 } 537 MasterQuotaManager quotaManager = master.getMasterQuotaManager(); 538 return quotaManager != null && quotaManager.isQuotaInitialized(); 539 } 540 }); 541 } 542 543 private void killActiveMaster() throws Exception { 544 UTIL.getHBaseCluster().getMaster(0).stop("Stopping to start again"); 545 UTIL.getHBaseCluster().waitOnMaster(0); 546 waitForQuotaInitialize(UTIL); 547 } 548 549 private NamespaceAuditor getQuotaManager() { 550 return UTIL.getHBaseCluster().getMaster().getMasterQuotaManager().getNamespaceQuotaManager(); 551 } 552 553 public static class MasterSyncObserver implements MasterCoprocessor, MasterObserver { 554 volatile CountDownLatch tableDeletionLatch; 555 static boolean throwExceptionInPreCreateTableAction; 556 557 @Override 558 public Optional<MasterObserver> getMasterObserver() { 559 return Optional.of(this); 560 } 561 562 @Override 563 public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx, 564 TableName tableName) throws IOException { 565 tableDeletionLatch = new CountDownLatch(1); 566 } 567 568 @Override 569 public void postCompletedDeleteTableAction( 570 final ObserverContext<MasterCoprocessorEnvironment> ctx, final TableName tableName) 571 throws IOException { 572 tableDeletionLatch.countDown(); 573 } 574 575 @Override 576 public void preCreateTableAction(ObserverContext<MasterCoprocessorEnvironment> ctx, 577 TableDescriptor desc, RegionInfo[] regions) throws IOException { 578 if (throwExceptionInPreCreateTableAction) { 579 throw new IOException("Throw exception as it is demanded."); 580 } 581 } 582 } 583 584 private void deleteTable(final TableName tableName) throws Exception { 585 // NOTE: We need a latch because admin is not sync, 586 // so the postOp coprocessor method may be called after the admin operation returned. 587 MasterSyncObserver observer = UTIL.getHBaseCluster().getMaster().getMasterCoprocessorHost() 588 .findCoprocessor(MasterSyncObserver.class); 589 ADMIN.deleteTable(tableName); 590 observer.tableDeletionLatch.await(); 591 } 592 593 @Test(expected = QuotaExceededException.class) 594 public void testExceedTableQuotaInNamespace() throws Exception { 595 String nsp = prefix + "_testExceedTableQuotaInNamespace"; 596 NamespaceDescriptor nspDesc = NamespaceDescriptor.create(nsp) 597 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "1").build(); 598 ADMIN.createNamespace(nspDesc); 599 assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(nsp)); 600 assertEquals(3, ADMIN.listNamespaceDescriptors().length); 601 HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); 602 HTableDescriptor tableDescOne = 603 new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1")); 604 tableDescOne.addFamily(fam1); 605 HTableDescriptor tableDescTwo = 606 new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2")); 607 tableDescTwo.addFamily(fam1); 608 ADMIN.createTable(tableDescOne); 609 ADMIN.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4); 610 } 611 612 @Test(expected = QuotaExceededException.class) 613 public void testCloneSnapshotQuotaExceed() throws Exception { 614 String nsp = prefix + "_testTableQuotaExceedWithCloneSnapshot"; 615 NamespaceDescriptor nspDesc = NamespaceDescriptor.create(nsp) 616 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "1").build(); 617 ADMIN.createNamespace(nspDesc); 618 assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(nsp)); 619 TableName tableName = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1"); 620 TableName cloneTableName = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2"); 621 HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); 622 HTableDescriptor tableDescOne = new HTableDescriptor(tableName); 623 tableDescOne.addFamily(fam1); 624 ADMIN.createTable(tableDescOne); 625 String snapshot = "snapshot_testTableQuotaExceedWithCloneSnapshot"; 626 ADMIN.snapshot(snapshot, tableName); 627 ADMIN.cloneSnapshot(snapshot, cloneTableName); 628 ADMIN.deleteSnapshot(snapshot); 629 } 630 631 @Test 632 public void testCloneSnapshot() throws Exception { 633 String nsp = prefix + "_testCloneSnapshot"; 634 NamespaceDescriptor nspDesc = 635 NamespaceDescriptor.create(nsp).addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2") 636 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "20").build(); 637 ADMIN.createNamespace(nspDesc); 638 assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(nsp)); 639 TableName tableName = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1"); 640 TableName cloneTableName = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2"); 641 642 HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); 643 HTableDescriptor tableDescOne = new HTableDescriptor(tableName); 644 tableDescOne.addFamily(fam1); 645 646 ADMIN.createTable(tableDescOne, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4); 647 String snapshot = "snapshot_testCloneSnapshot"; 648 ADMIN.snapshot(snapshot, tableName); 649 ADMIN.cloneSnapshot(snapshot, cloneTableName); 650 651 int tableLength; 652 try (RegionLocator locator = ADMIN.getConnection().getRegionLocator(tableName)) { 653 tableLength = locator.getStartKeys().length; 654 } 655 assertEquals(tableName.getNameAsString() + " should have four regions.", 4, tableLength); 656 657 try (RegionLocator locator = ADMIN.getConnection().getRegionLocator(cloneTableName)) { 658 tableLength = locator.getStartKeys().length; 659 } 660 assertEquals(cloneTableName.getNameAsString() + " should have four regions.", 4, tableLength); 661 662 NamespaceTableAndRegionInfo nstate = getNamespaceState(nsp); 663 assertEquals("Total tables count should be 2.", 2, nstate.getTables().size()); 664 assertEquals("Total regions count should be.", 8, nstate.getRegionCount()); 665 666 ADMIN.deleteSnapshot(snapshot); 667 } 668 669 @Test 670 public void testRestoreSnapshot() throws Exception { 671 String nsp = prefix + "_testRestoreSnapshot"; 672 NamespaceDescriptor nspDesc = NamespaceDescriptor.create(nsp) 673 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "10").build(); 674 ADMIN.createNamespace(nspDesc); 675 assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(nsp)); 676 TableName tableName1 = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1"); 677 HTableDescriptor tableDescOne = new HTableDescriptor(tableName1); 678 HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); 679 tableDescOne.addFamily(fam1); 680 ADMIN.createTable(tableDescOne, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4); 681 682 NamespaceTableAndRegionInfo nstate = getNamespaceState(nsp); 683 assertEquals("Intial region count should be 4.", 4, nstate.getRegionCount()); 684 685 String snapshot = "snapshot_testRestoreSnapshot"; 686 ADMIN.snapshot(snapshot, tableName1); 687 688 List<HRegionInfo> regions = ADMIN.getTableRegions(tableName1); 689 Collections.sort(regions); 690 691 ADMIN.split(tableName1, Bytes.toBytes("JJJ")); 692 Thread.sleep(2000); 693 assertEquals("Total regions count should be 5.", 5, nstate.getRegionCount()); 694 695 ADMIN.disableTable(tableName1); 696 ADMIN.restoreSnapshot(snapshot); 697 698 assertEquals("Total regions count should be 4 after restore.", 4, nstate.getRegionCount()); 699 700 ADMIN.enableTable(tableName1); 701 ADMIN.deleteSnapshot(snapshot); 702 } 703 704 @Test 705 public void testRestoreSnapshotQuotaExceed() throws Exception { 706 String nsp = prefix + "_testRestoreSnapshotQuotaExceed"; 707 NamespaceDescriptor nspDesc = NamespaceDescriptor.create(nsp) 708 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "10").build(); 709 ADMIN.createNamespace(nspDesc); 710 NamespaceDescriptor ndesc = ADMIN.getNamespaceDescriptor(nsp); 711 assertNotNull("Namespace descriptor found null.", ndesc); 712 TableName tableName1 = TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1"); 713 HTableDescriptor tableDescOne = new HTableDescriptor(tableName1); 714 HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); 715 tableDescOne.addFamily(fam1); 716 717 ADMIN.createTable(tableDescOne, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4); 718 719 NamespaceTableAndRegionInfo nstate = getNamespaceState(nsp); 720 assertEquals("Intial region count should be 4.", 4, nstate.getRegionCount()); 721 722 String snapshot = "snapshot_testRestoreSnapshotQuotaExceed"; 723 // snapshot has 4 regions 724 ADMIN.snapshot(snapshot, tableName1); 725 // recreate table with 1 region and set max regions to 3 for namespace 726 ADMIN.disableTable(tableName1); 727 ADMIN.deleteTable(tableName1); 728 ADMIN.createTable(tableDescOne); 729 ndesc.setConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "3"); 730 ADMIN.modifyNamespace(ndesc); 731 732 ADMIN.disableTable(tableName1); 733 try { 734 ADMIN.restoreSnapshot(snapshot); 735 fail("Region quota is exceeded so QuotaExceededException should be thrown but HBaseAdmin" 736 + " wraps IOException into RestoreSnapshotException"); 737 } catch (RestoreSnapshotException ignore) { 738 assertTrue(ignore.getCause() instanceof QuotaExceededException); 739 } 740 assertEquals(1, getNamespaceState(nsp).getRegionCount()); 741 ADMIN.enableTable(tableName1); 742 ADMIN.deleteSnapshot(snapshot); 743 } 744}