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