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.RegionInfo; 056import org.apache.hadoop.hbase.client.RegionLocator; 057import org.apache.hadoop.hbase.client.Table; 058import org.apache.hadoop.hbase.client.TableDescriptor; 059import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 060import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 061import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 062import org.apache.hadoop.hbase.coprocessor.MasterObserver; 063import org.apache.hadoop.hbase.coprocessor.ObserverContext; 064import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor; 065import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 066import org.apache.hadoop.hbase.coprocessor.RegionObserver; 067import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessor; 068import org.apache.hadoop.hbase.coprocessor.RegionServerObserver; 069import org.apache.hadoop.hbase.master.HMaster; 070import org.apache.hadoop.hbase.master.MasterCoprocessorHost; 071import org.apache.hadoop.hbase.master.TableNamespaceManager; 072import org.apache.hadoop.hbase.quotas.MasterQuotaManager; 073import org.apache.hadoop.hbase.quotas.QuotaExceededException; 074import org.apache.hadoop.hbase.quotas.QuotaUtil; 075import org.apache.hadoop.hbase.regionserver.HRegion; 076import org.apache.hadoop.hbase.regionserver.Store; 077import org.apache.hadoop.hbase.regionserver.StoreFile; 078import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker; 079import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest; 080import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException; 081import org.apache.hadoop.hbase.testclassification.LargeTests; 082import org.apache.hadoop.hbase.util.Bytes; 083import org.apache.hadoop.hbase.util.CommonFSUtils; 084import org.apache.zookeeper.KeeperException; 085import org.junit.After; 086import org.junit.AfterClass; 087import org.junit.BeforeClass; 088import org.junit.ClassRule; 089import org.junit.Test; 090import org.junit.experimental.categories.Category; 091import org.slf4j.Logger; 092import org.slf4j.LoggerFactory; 093 094@Category(LargeTests.class) 095public class TestNamespaceAuditor { 096 097 @ClassRule 098 public static final HBaseClassTestRule CLASS_RULE = 099 HBaseClassTestRule.forClass(TestNamespaceAuditor.class); 100 101 private static final Logger LOG = LoggerFactory.getLogger(TestNamespaceAuditor.class); 102 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 103 private static Admin ADMIN; 104 private String prefix = "TestNamespaceAuditor"; 105 106 @BeforeClass 107 public static void before() throws Exception { 108 Configuration conf = UTIL.getConfiguration(); 109 conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, CustomObserver.class.getName()); 110 conf.setStrings(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, MasterSyncObserver.class.getName(), 111 CPMasterObserver.class.getName()); 112 conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 5); 113 conf.setBoolean(QuotaUtil.QUOTA_CONF_KEY, true); 114 conf.setClass("hbase.coprocessor.regionserver.classes", CPRegionServerObserver.class, 115 RegionServerObserver.class); 116 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(2).build(); 117 UTIL.startMiniCluster(option); 118 waitForQuotaInitialize(UTIL); 119 ADMIN = UTIL.getAdmin(); 120 } 121 122 @AfterClass 123 public static void tearDown() throws Exception { 124 UTIL.shutdownMiniCluster(); 125 } 126 127 @After 128 public void cleanup() throws Exception, KeeperException { 129 for (HTableDescriptor table : ADMIN.listTables()) { 130 ADMIN.disableTable(table.getTableName()); 131 deleteTable(table.getTableName()); 132 } 133 for (NamespaceDescriptor ns : ADMIN.listNamespaceDescriptors()) { 134 if (ns.getName().startsWith(prefix)) { 135 ADMIN.deleteNamespace(ns.getName()); 136 } 137 } 138 assertTrue("Quota manager not initialized", 139 UTIL.getHBaseCluster().getMaster().getMasterQuotaManager().isQuotaInitialized()); 140 } 141 142 @Test 143 public void testTableOperations() throws Exception { 144 String nsp = prefix + "_np2"; 145 NamespaceDescriptor nspDesc = 146 NamespaceDescriptor.create(nsp).addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "5") 147 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build(); 148 ADMIN.createNamespace(nspDesc); 149 assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(nsp)); 150 assertEquals(3, ADMIN.listNamespaceDescriptors().length); 151 HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); 152 153 HTableDescriptor tableDescOne = 154 new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table1")); 155 tableDescOne.addFamily(fam1); 156 HTableDescriptor tableDescTwo = 157 new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table2")); 158 tableDescTwo.addFamily(fam1); 159 HTableDescriptor tableDescThree = 160 new HTableDescriptor(TableName.valueOf(nsp + TableName.NAMESPACE_DELIM + "table3")); 161 tableDescThree.addFamily(fam1); 162 ADMIN.createTable(tableDescOne); 163 boolean constraintViolated = false; 164 try { 165 ADMIN.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 5); 166 } catch (Exception exp) { 167 assertTrue(exp instanceof IOException); 168 constraintViolated = true; 169 } finally { 170 assertTrue("Constraint not violated for table " + tableDescTwo.getTableName(), 171 constraintViolated); 172 } 173 ADMIN.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 4); 174 NamespaceTableAndRegionInfo nspState = getQuotaManager().getState(nsp); 175 assertNotNull(nspState); 176 assertTrue(nspState.getTables().size() == 2); 177 assertTrue(nspState.getRegionCount() == 5); 178 constraintViolated = false; 179 try { 180 ADMIN.createTable(tableDescThree); 181 } catch (Exception exp) { 182 assertTrue(exp instanceof IOException); 183 constraintViolated = true; 184 } finally { 185 assertTrue("Constraint not violated for table " + tableDescThree.getTableName(), 186 constraintViolated); 187 } 188 } 189 190 @Test 191 public void testValidQuotas() throws Exception { 192 boolean exceptionCaught = false; 193 FileSystem fs = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getFileSystem(); 194 Path rootDir = UTIL.getHBaseCluster().getMaster().getMasterFileSystem().getRootDir(); 195 NamespaceDescriptor nspDesc = NamespaceDescriptor.create(prefix + "vq1") 196 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "hihdufh") 197 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build(); 198 try { 199 ADMIN.createNamespace(nspDesc); 200 } catch (Exception exp) { 201 LOG.warn(exp.toString(), exp); 202 exceptionCaught = true; 203 } finally { 204 assertTrue(exceptionCaught); 205 assertFalse(fs.exists(CommonFSUtils.getNamespaceDir(rootDir, nspDesc.getName()))); 206 } 207 nspDesc = NamespaceDescriptor.create(prefix + "vq2") 208 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "-456") 209 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build(); 210 try { 211 ADMIN.createNamespace(nspDesc); 212 } catch (Exception exp) { 213 LOG.warn(exp.toString(), exp); 214 exceptionCaught = true; 215 } finally { 216 assertTrue(exceptionCaught); 217 assertFalse(fs.exists(CommonFSUtils.getNamespaceDir(rootDir, nspDesc.getName()))); 218 } 219 nspDesc = NamespaceDescriptor.create(prefix + "vq3") 220 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "10") 221 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "sciigd").build(); 222 try { 223 ADMIN.createNamespace(nspDesc); 224 } catch (Exception exp) { 225 LOG.warn(exp.toString(), exp); 226 exceptionCaught = true; 227 } finally { 228 assertTrue(exceptionCaught); 229 assertFalse(fs.exists(CommonFSUtils.getNamespaceDir(rootDir, nspDesc.getName()))); 230 } 231 nspDesc = NamespaceDescriptor.create(prefix + "vq4") 232 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "10") 233 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "-1500").build(); 234 try { 235 ADMIN.createNamespace(nspDesc); 236 } catch (Exception exp) { 237 LOG.warn(exp.toString(), exp); 238 exceptionCaught = true; 239 } finally { 240 assertTrue(exceptionCaught); 241 assertFalse(fs.exists(CommonFSUtils.getNamespaceDir(rootDir, nspDesc.getName()))); 242 } 243 } 244 245 @Test 246 public void testDeleteTable() throws Exception { 247 String namespace = prefix + "_dummy"; 248 NamespaceDescriptor nspDesc = NamespaceDescriptor.create(namespace) 249 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "100") 250 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "3").build(); 251 ADMIN.createNamespace(nspDesc); 252 assertNotNull("Namespace descriptor found null.", ADMIN.getNamespaceDescriptor(namespace)); 253 NamespaceTableAndRegionInfo stateInfo = getNamespaceState(nspDesc.getName()); 254 assertNotNull("Namespace state found null for " + namespace, stateInfo); 255 HColumnDescriptor fam1 = new HColumnDescriptor("fam1"); 256 HTableDescriptor tableDescOne = 257 new HTableDescriptor(TableName.valueOf(namespace + TableName.NAMESPACE_DELIM + "table1")); 258 tableDescOne.addFamily(fam1); 259 HTableDescriptor tableDescTwo = 260 new HTableDescriptor(TableName.valueOf(namespace + TableName.NAMESPACE_DELIM + "table2")); 261 tableDescTwo.addFamily(fam1); 262 ADMIN.createTable(tableDescOne); 263 ADMIN.createTable(tableDescTwo, Bytes.toBytes("AAA"), Bytes.toBytes("ZZZ"), 5); 264 stateInfo = getNamespaceState(nspDesc.getName()); 265 assertNotNull("Namespace state found to be null.", stateInfo); 266 assertEquals(2, stateInfo.getTables().size()); 267 assertEquals(5, stateInfo.getRegionCountOfTable(tableDescTwo.getTableName())); 268 assertEquals(6, stateInfo.getRegionCount()); 269 ADMIN.disableTable(tableDescOne.getTableName()); 270 deleteTable(tableDescOne.getTableName()); 271 stateInfo = getNamespaceState(nspDesc.getName()); 272 assertNotNull("Namespace state found to be null.", stateInfo); 273 assertEquals(5, stateInfo.getRegionCount()); 274 assertEquals(1, stateInfo.getTables().size()); 275 ADMIN.disableTable(tableDescTwo.getTableName()); 276 deleteTable(tableDescTwo.getTableName()); 277 ADMIN.deleteNamespace(namespace); 278 stateInfo = getNamespaceState(namespace); 279 assertNull("Namespace state not found to be null.", stateInfo); 280 } 281 282 public static class CPRegionServerObserver 283 implements RegionServerCoprocessor, RegionServerObserver { 284 private volatile boolean shouldFailMerge = false; 285 286 public void failMerge(boolean fail) { 287 shouldFailMerge = fail; 288 } 289 290 private boolean triggered = false; 291 292 public synchronized void waitUtilTriggered() throws InterruptedException { 293 while (!triggered) { 294 wait(); 295 } 296 } 297 298 @Override 299 public Optional<RegionServerObserver> getRegionServerObserver() { 300 return Optional.of(this); 301 } 302 } 303 304 public static class CPMasterObserver implements MasterCoprocessor, MasterObserver { 305 private volatile boolean shouldFailMerge = false; 306 307 public void failMerge(boolean fail) { 308 shouldFailMerge = fail; 309 } 310 311 @Override 312 public Optional<MasterObserver> getMasterObserver() { 313 return Optional.of(this); 314 } 315 316 @Override 317 public synchronized void preMergeRegionsAction( 318 final ObserverContext<MasterCoprocessorEnvironment> ctx, final RegionInfo[] regionsToMerge) 319 throws IOException { 320 notifyAll(); 321 if (shouldFailMerge) { 322 throw new IOException("fail merge"); 323 } 324 } 325 } 326 327 @Test 328 public void testRegionMerge() throws Exception { 329 String nsp1 = prefix + "_regiontest"; 330 final int initialRegions = 3; 331 NamespaceDescriptor nspDesc = NamespaceDescriptor.create(nsp1) 332 .addConfiguration(TableNamespaceManager.KEY_MAX_REGIONS, "" + initialRegions) 333 .addConfiguration(TableNamespaceManager.KEY_MAX_TABLES, "2").build(); 334 ADMIN.createNamespace(nspDesc); 335 final TableName tableTwo = TableName.valueOf(nsp1 + TableName.NAMESPACE_DELIM + "table2"); 336 byte[] columnFamily = Bytes.toBytes("info"); 337 HTableDescriptor tableDescOne = new HTableDescriptor(tableTwo); 338 tableDescOne.addFamily(new HColumnDescriptor(columnFamily)); 339 ADMIN.createTable(tableDescOne, Bytes.toBytes("0"), Bytes.toBytes("9"), initialRegions); 340 Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration()); 341 try (Table table = connection.getTable(tableTwo)) { 342 UTIL.loadNumericRows(table, Bytes.toBytes("info"), 1000, 1999); 343 } 344 ADMIN.flush(tableTwo); 345 List<RegionInfo> hris = ADMIN.getRegions(tableTwo); 346 assertEquals(initialRegions, hris.size()); 347 Collections.sort(hris, RegionInfo.COMPARATOR); 348 Future<?> f = ADMIN.mergeRegionsAsync(hris.get(0).getEncodedNameAsBytes(), 349 hris.get(1).getEncodedNameAsBytes(), false); 350 f.get(10, TimeUnit.SECONDS); 351 352 hris = ADMIN.getRegions(tableTwo); 353 assertEquals(initialRegions - 1, hris.size()); 354 Collections.sort(hris, RegionInfo.COMPARATOR); 355 byte[] splitKey = Bytes.toBytes("3"); 356 HRegion regionToSplit = UTIL.getMiniHBaseCluster().getRegions(tableTwo).stream() 357 .filter(r -> r.getRegionInfo().containsRow(splitKey)).findFirst().get(); 358 regionToSplit.compact(true); 359 // Waiting for compaction to finish 360 UTIL.waitFor(30000, new Waiter.Predicate<Exception>() { 361 @Override 362 public boolean evaluate() throws Exception { 363 return (CompactionState.NONE 364 == ADMIN.getCompactionStateForRegion(regionToSplit.getRegionInfo().getRegionName())); 365 } 366 }); 367 368 // Cleaning compacted references for split to proceed 369 regionToSplit.getStores().stream().forEach(s -> { 370 try { 371 s.closeAndArchiveCompactedFiles(); 372 } catch (IOException e1) { 373 LOG.error("Error whiling cleaning compacted file"); 374 } 375 }); 376 // the above compact may quit immediately if there is a compaction ongoing, so here we need to 377 // wait a while to let the ongoing compaction finish. 378 UTIL.waitFor(10000, regionToSplit::isSplittable); 379 ADMIN.splitRegionAsync(regionToSplit.getRegionInfo().getRegionName(), splitKey).get(10, 380 TimeUnit.SECONDS); 381 hris = ADMIN.getRegions(tableTwo); 382 assertEquals(initialRegions, hris.size()); 383 Collections.sort(hris, RegionInfo.COMPARATOR); 384 385 // Fail region merge through Coprocessor hook 386 MiniHBaseCluster cluster = UTIL.getHBaseCluster(); 387 MasterCoprocessorHost cpHost = cluster.getMaster().getMasterCoprocessorHost(); 388 Coprocessor coprocessor = cpHost.findCoprocessor(CPMasterObserver.class); 389 CPMasterObserver masterObserver = (CPMasterObserver) coprocessor; 390 masterObserver.failMerge(true); 391 392 f = ADMIN.mergeRegionsAsync(hris.get(1).getEncodedNameAsBytes(), 393 hris.get(2).getEncodedNameAsBytes(), false); 394 try { 395 f.get(10, TimeUnit.SECONDS); 396 fail("Merge was supposed to fail!"); 397 } catch (ExecutionException ee) { 398 // Expected. 399 } 400 hris = ADMIN.getRegions(tableTwo); 401 assertEquals(initialRegions, hris.size()); 402 Collections.sort(hris, RegionInfo.COMPARATOR); 403 // verify that we cannot split 404 try { 405 ADMIN.splitRegionAsync(hris.get(1).getRegionName(), Bytes.toBytes("6")).get(10, 406 TimeUnit.SECONDS); 407 fail(); 408 } catch (ExecutionException 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}