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