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