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.quotas; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNull; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.util.ArrayList; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030import java.util.Map.Entry; 031import java.util.concurrent.atomic.AtomicLong; 032import org.apache.hadoop.conf.Configuration; 033import org.apache.hadoop.fs.FileStatus; 034import org.apache.hadoop.fs.FileSystem; 035import org.apache.hadoop.fs.Path; 036import org.apache.hadoop.hbase.DoNotRetryIOException; 037import org.apache.hadoop.hbase.HBaseClassTestRule; 038import org.apache.hadoop.hbase.HBaseTestingUtility; 039import org.apache.hadoop.hbase.HConstants; 040import org.apache.hadoop.hbase.TableName; 041import org.apache.hadoop.hbase.TableNotEnabledException; 042import org.apache.hadoop.hbase.client.Admin; 043import org.apache.hadoop.hbase.client.Append; 044import org.apache.hadoop.hbase.client.ClientServiceCallable; 045import org.apache.hadoop.hbase.client.Connection; 046import org.apache.hadoop.hbase.client.Delete; 047import org.apache.hadoop.hbase.client.Increment; 048import org.apache.hadoop.hbase.client.Mutation; 049import org.apache.hadoop.hbase.client.Put; 050import org.apache.hadoop.hbase.client.RegionInfo; 051import org.apache.hadoop.hbase.client.Result; 052import org.apache.hadoop.hbase.client.ResultScanner; 053import org.apache.hadoop.hbase.client.RpcRetryingCaller; 054import org.apache.hadoop.hbase.client.RpcRetryingCallerFactory; 055import org.apache.hadoop.hbase.client.Scan; 056import org.apache.hadoop.hbase.client.SecureBulkLoadClient; 057import org.apache.hadoop.hbase.client.Table; 058import org.apache.hadoop.hbase.ipc.RpcControllerFactory; 059import org.apache.hadoop.hbase.master.HMaster; 060import org.apache.hadoop.hbase.quotas.policies.DefaultViolationPolicyEnforcement; 061import org.apache.hadoop.hbase.regionserver.HRegionServer; 062import org.apache.hadoop.hbase.regionserver.TestHRegionServerBulkLoad; 063import org.apache.hadoop.hbase.security.AccessDeniedException; 064import org.apache.hadoop.hbase.testclassification.LargeTests; 065import org.apache.hadoop.hbase.util.Bytes; 066import org.apache.hadoop.hbase.util.Pair; 067import org.apache.hadoop.util.StringUtils; 068import org.junit.AfterClass; 069import org.junit.Before; 070import org.junit.BeforeClass; 071import org.junit.ClassRule; 072import org.junit.Rule; 073import org.junit.Test; 074import org.junit.experimental.categories.Category; 075import org.junit.rules.TestName; 076import org.slf4j.Logger; 077import org.slf4j.LoggerFactory; 078 079/** 080 * End-to-end test class for filesystem space quotas. 081 */ 082@Category(LargeTests.class) 083public class TestSpaceQuotas { 084 085 @ClassRule 086 public static final HBaseClassTestRule CLASS_RULE = 087 HBaseClassTestRule.forClass(TestSpaceQuotas.class); 088 089 private static final Logger LOG = LoggerFactory.getLogger(TestSpaceQuotas.class); 090 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 091 // Global for all tests in the class 092 private static final AtomicLong COUNTER = new AtomicLong(0); 093 private static final int NUM_RETRIES = 10; 094 095 @Rule 096 public TestName testName = new TestName(); 097 private SpaceQuotaHelperForTests helper; 098 private final TableName NON_EXISTENT_TABLE = TableName.valueOf("NON_EXISTENT_TABLE"); 099 100 @BeforeClass 101 public static void setUp() throws Exception { 102 Configuration conf = TEST_UTIL.getConfiguration(); 103 SpaceQuotaHelperForTests.updateConfigForQuotas(conf); 104 TEST_UTIL.startMiniCluster(1); 105 } 106 107 @AfterClass 108 public static void tearDown() throws Exception { 109 TEST_UTIL.shutdownMiniCluster(); 110 } 111 112 @Before 113 public void removeAllQuotas() throws Exception { 114 final Connection conn = TEST_UTIL.getConnection(); 115 if (helper == null) { 116 helper = new SpaceQuotaHelperForTests(TEST_UTIL, testName, COUNTER); 117 } 118 // Wait for the quota table to be created 119 if (!conn.getAdmin().tableExists(QuotaUtil.QUOTA_TABLE_NAME)) { 120 helper.waitForQuotaTable(conn); 121 } else { 122 // Or, clean up any quotas from previous test runs. 123 helper.removeAllQuotas(conn); 124 assertEquals(0, helper.listNumDefinedQuotas(conn)); 125 } 126 } 127 128 @Test 129 public void testNoInsertsWithPut() throws Exception { 130 Put p = new Put(Bytes.toBytes("to_reject")); 131 p.addColumn( 132 Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), Bytes.toBytes("reject")); 133 writeUntilViolationAndVerifyViolation(SpaceViolationPolicy.NO_INSERTS, p); 134 } 135 136 @Test 137 public void testNoInsertsWithAppend() throws Exception { 138 Append a = new Append(Bytes.toBytes("to_reject")); 139 a.addColumn( 140 Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), Bytes.toBytes("reject")); 141 writeUntilViolationAndVerifyViolation(SpaceViolationPolicy.NO_INSERTS, a); 142 } 143 144 @Test 145 public void testNoInsertsWithIncrement() throws Exception { 146 Increment i = new Increment(Bytes.toBytes("to_reject")); 147 i.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("count"), 0); 148 writeUntilViolationAndVerifyViolation(SpaceViolationPolicy.NO_INSERTS, i); 149 } 150 151 @Test 152 public void testDeletesAfterNoInserts() throws Exception { 153 final TableName tn = writeUntilViolation(SpaceViolationPolicy.NO_INSERTS); 154 // Try a couple of times to verify that the quota never gets enforced, same as we 155 // do when we're trying to catch the failure. 156 Delete d = new Delete(Bytes.toBytes("should_not_be_rejected")); 157 for (int i = 0; i < NUM_RETRIES; i++) { 158 try (Table t = TEST_UTIL.getConnection().getTable(tn)) { 159 t.delete(d); 160 } 161 } 162 } 163 164 @Test 165 public void testNoWritesWithPut() throws Exception { 166 Put p = new Put(Bytes.toBytes("to_reject")); 167 p.addColumn( 168 Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), Bytes.toBytes("reject")); 169 writeUntilViolationAndVerifyViolation(SpaceViolationPolicy.NO_WRITES, p); 170 } 171 172 @Test 173 public void testNoWritesWithAppend() throws Exception { 174 Append a = new Append(Bytes.toBytes("to_reject")); 175 a.addColumn( 176 Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), Bytes.toBytes("reject")); 177 writeUntilViolationAndVerifyViolation(SpaceViolationPolicy.NO_WRITES, a); 178 } 179 180 @Test 181 public void testNoWritesWithIncrement() throws Exception { 182 Increment i = new Increment(Bytes.toBytes("to_reject")); 183 i.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("count"), 0); 184 writeUntilViolationAndVerifyViolation(SpaceViolationPolicy.NO_WRITES, i); 185 } 186 187 @Test 188 public void testNoWritesWithDelete() throws Exception { 189 Delete d = new Delete(Bytes.toBytes("to_reject")); 190 writeUntilViolationAndVerifyViolation(SpaceViolationPolicy.NO_WRITES, d); 191 } 192 193 @Test 194 public void testNoCompactions() throws Exception { 195 Put p = new Put(Bytes.toBytes("to_reject")); 196 p.addColumn( 197 Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), Bytes.toBytes("reject")); 198 final TableName tn = writeUntilViolationAndVerifyViolation( 199 SpaceViolationPolicy.NO_WRITES_COMPACTIONS, p); 200 // We know the policy is active at this point 201 202 // Major compactions should be rejected 203 try { 204 TEST_UTIL.getAdmin().majorCompact(tn); 205 fail("Expected that invoking the compaction should throw an Exception"); 206 } catch (DoNotRetryIOException e) { 207 // Expected! 208 } 209 // Minor compactions should also be rejected. 210 try { 211 TEST_UTIL.getAdmin().compact(tn); 212 fail("Expected that invoking the compaction should throw an Exception"); 213 } catch (DoNotRetryIOException e) { 214 // Expected! 215 } 216 } 217 218 @Test 219 public void testNoEnableAfterDisablePolicy() throws Exception { 220 Put p = new Put(Bytes.toBytes("to_reject")); 221 p.addColumn( 222 Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), Bytes.toBytes("reject")); 223 final TableName tn = writeUntilViolation(SpaceViolationPolicy.DISABLE); 224 final Admin admin = TEST_UTIL.getAdmin(); 225 // Disabling a table relies on some external action (over the other policies), so wait a bit 226 // more than the other tests. 227 for (int i = 0; i < NUM_RETRIES * 2; i++) { 228 if (admin.isTableEnabled(tn)) { 229 LOG.info(tn + " is still enabled, expecting it to be disabled. Will wait and re-check."); 230 Thread.sleep(2000); 231 } 232 } 233 assertFalse(tn + " is still enabled but it should be disabled", admin.isTableEnabled(tn)); 234 try { 235 admin.enableTable(tn); 236 } catch (AccessDeniedException e) { 237 String exceptionContents = StringUtils.stringifyException(e); 238 final String expectedText = "violated space quota"; 239 assertTrue("Expected the exception to contain " + expectedText + ", but was: " 240 + exceptionContents, exceptionContents.contains(expectedText)); 241 } 242 } 243 244 @Test 245 public void testNoBulkLoadsWithNoWrites() throws Exception { 246 Put p = new Put(Bytes.toBytes("to_reject")); 247 p.addColumn( 248 Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), Bytes.toBytes("reject")); 249 TableName tableName = writeUntilViolationAndVerifyViolation(SpaceViolationPolicy.NO_WRITES, p); 250 251 // The table is now in violation. Try to do a bulk load 252 ClientServiceCallable<Void> callable = generateFileToLoad(tableName, 1, 50); 253 RpcRetryingCallerFactory factory = new RpcRetryingCallerFactory(TEST_UTIL.getConfiguration()); 254 RpcRetryingCaller<Void> caller = factory.<Void> newCaller(); 255 try { 256 caller.callWithRetries(callable, Integer.MAX_VALUE); 257 fail("Expected the bulk load call to fail!"); 258 } catch (SpaceLimitingException e) { 259 // Pass 260 LOG.trace("Caught expected exception", e); 261 } 262 } 263 264 @Test 265 public void testAtomicBulkLoadUnderQuota() throws Exception { 266 // Need to verify that if the batch of hfiles cannot be loaded, none are loaded. 267 TableName tn = helper.createTableWithRegions(10); 268 269 final long sizeLimit = 50L * SpaceQuotaHelperForTests.ONE_KILOBYTE; 270 QuotaSettings settings = QuotaSettingsFactory.limitTableSpace( 271 tn, sizeLimit, SpaceViolationPolicy.NO_INSERTS); 272 TEST_UTIL.getAdmin().setQuota(settings); 273 274 HRegionServer rs = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0); 275 RegionServerSpaceQuotaManager spaceQuotaManager = rs.getRegionServerSpaceQuotaManager(); 276 Map<TableName,SpaceQuotaSnapshot> snapshots = spaceQuotaManager.copyQuotaSnapshots(); 277 Map<RegionInfo,Long> regionSizes = getReportedSizesForTable(tn); 278 while (true) { 279 SpaceQuotaSnapshot snapshot = snapshots.get(tn); 280 if (snapshot != null && snapshot.getLimit() > 0) { 281 break; 282 } 283 LOG.debug( 284 "Snapshot does not yet realize quota limit: " + snapshots + ", regionsizes: " + 285 regionSizes); 286 Thread.sleep(3000); 287 snapshots = spaceQuotaManager.copyQuotaSnapshots(); 288 regionSizes = getReportedSizesForTable(tn); 289 } 290 // Our quota limit should be reflected in the latest snapshot 291 SpaceQuotaSnapshot snapshot = snapshots.get(tn); 292 assertEquals(0L, snapshot.getUsage()); 293 assertEquals(sizeLimit, snapshot.getLimit()); 294 295 // We would also not have a "real" policy in violation 296 ActivePolicyEnforcement activePolicies = spaceQuotaManager.getActiveEnforcements(); 297 SpaceViolationPolicyEnforcement enforcement = activePolicies.getPolicyEnforcement(tn); 298 assertTrue( 299 "Expected to find Noop policy, but got " + enforcement.getClass().getSimpleName(), 300 enforcement instanceof DefaultViolationPolicyEnforcement); 301 302 // Should generate two files, each of which is over 25KB each 303 ClientServiceCallable<Void> callable = generateFileToLoad(tn, 2, 500); 304 FileSystem fs = TEST_UTIL.getTestFileSystem(); 305 FileStatus[] files = fs.listStatus( 306 new Path(fs.getHomeDirectory(), testName.getMethodName() + "_files")); 307 for (FileStatus file : files) { 308 assertTrue( 309 "Expected the file, " + file.getPath() + ", length to be larger than 25KB, but was " 310 + file.getLen(), 311 file.getLen() > 25 * SpaceQuotaHelperForTests.ONE_KILOBYTE); 312 LOG.debug(file.getPath() + " -> " + file.getLen() +"B"); 313 } 314 315 RpcRetryingCallerFactory factory = new RpcRetryingCallerFactory(TEST_UTIL.getConfiguration()); 316 RpcRetryingCaller<Void> caller = factory.<Void> newCaller(); 317 try { 318 caller.callWithRetries(callable, Integer.MAX_VALUE); 319 fail("Expected the bulk load call to fail!"); 320 } catch (SpaceLimitingException e) { 321 // Pass 322 LOG.trace("Caught expected exception", e); 323 } 324 // Verify that we have no data in the table because neither file should have been 325 // loaded even though one of the files could have. 326 Table table = TEST_UTIL.getConnection().getTable(tn); 327 ResultScanner scanner = table.getScanner(new Scan()); 328 try { 329 assertNull("Expected no results", scanner.next()); 330 } finally{ 331 scanner.close(); 332 } 333 } 334 335 @Test 336 public void testTableQuotaOverridesNamespaceQuota() throws Exception { 337 final SpaceViolationPolicy policy = SpaceViolationPolicy.NO_INSERTS; 338 final TableName tn = helper.createTableWithRegions(10); 339 340 // 2MB limit on the table, 1GB limit on the namespace 341 final long tableLimit = 2L * SpaceQuotaHelperForTests.ONE_MEGABYTE; 342 final long namespaceLimit = 1024L * SpaceQuotaHelperForTests.ONE_MEGABYTE; 343 TEST_UTIL.getAdmin().setQuota(QuotaSettingsFactory.limitTableSpace(tn, tableLimit, policy)); 344 TEST_UTIL.getAdmin().setQuota(QuotaSettingsFactory.limitNamespaceSpace( 345 tn.getNamespaceAsString(), namespaceLimit, policy)); 346 347 // Write more data than should be allowed and flush it to disk 348 helper.writeData(tn, 3L * SpaceQuotaHelperForTests.ONE_MEGABYTE); 349 350 // This should be sufficient time for the chores to run and see the change. 351 Thread.sleep(5000); 352 353 // The write should be rejected because the table quota takes priority over the namespace 354 Put p = new Put(Bytes.toBytes("to_reject")); 355 p.addColumn( 356 Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), Bytes.toBytes("reject")); 357 verifyViolation(policy, tn, p); 358 } 359 360 @Test 361 public void testSetQuotaAndThenRemoveWithNoInserts() throws Exception { 362 setQuotaAndThenRemove(SpaceViolationPolicy.NO_INSERTS); 363 } 364 365 @Test 366 public void testSetQuotaAndThenRemoveWithNoWrite() throws Exception { 367 setQuotaAndThenRemove(SpaceViolationPolicy.NO_WRITES); 368 } 369 370 @Test 371 public void testSetQuotaAndThenRemoveWithNoWritesCompactions() throws Exception { 372 setQuotaAndThenRemove(SpaceViolationPolicy.NO_WRITES_COMPACTIONS); 373 } 374 375 @Test 376 public void testSetQuotaAndThenRemoveWithDisable() throws Exception { 377 setQuotaAndThenRemove(SpaceViolationPolicy.DISABLE); 378 } 379 380 @Test 381 public void testSetQuotaAndThenDropTableWithNoInserts() throws Exception { 382 setQuotaAndThenDropTable(SpaceViolationPolicy.NO_INSERTS); 383 } 384 385 @Test 386 public void testSetQuotaAndThenDropTableWithNoWrite() throws Exception { 387 setQuotaAndThenDropTable(SpaceViolationPolicy.NO_WRITES); 388 } 389 390 @Test 391 public void testSetQuotaAndThenDropTableeWithNoWritesCompactions() throws Exception { 392 setQuotaAndThenDropTable(SpaceViolationPolicy.NO_WRITES_COMPACTIONS); 393 } 394 395 @Test 396 public void testSetQuotaAndThenDropTableWithDisable() throws Exception { 397 setQuotaAndThenDropTable(SpaceViolationPolicy.DISABLE); 398 } 399 400 @Test 401 public void testSetQuotaAndThenIncreaseQuotaWithNoInserts() throws Exception { 402 setQuotaAndThenIncreaseQuota(SpaceViolationPolicy.NO_INSERTS); 403 } 404 405 @Test 406 public void testSetQuotaAndThenIncreaseQuotaWithNoWrite() throws Exception { 407 setQuotaAndThenIncreaseQuota(SpaceViolationPolicy.NO_WRITES); 408 } 409 410 @Test 411 public void testSetQuotaAndThenIncreaseQuotaWithNoWritesCompactions() throws Exception { 412 setQuotaAndThenIncreaseQuota(SpaceViolationPolicy.NO_WRITES_COMPACTIONS); 413 } 414 415 @Test 416 public void testSetQuotaAndThenRemoveInOneWithNoInserts() throws Exception { 417 setQuotaAndThenRemoveInOneAmongTwoTables(SpaceViolationPolicy.NO_INSERTS); 418 } 419 420 @Test 421 public void testSetQuotaAndThenRemoveInOneWithNoWrite() throws Exception { 422 setQuotaAndThenRemoveInOneAmongTwoTables(SpaceViolationPolicy.NO_WRITES); 423 } 424 425 @Test 426 public void testSetQuotaAndThenRemoveInOneWithNoWritesCompaction() throws Exception { 427 setQuotaAndThenRemoveInOneAmongTwoTables(SpaceViolationPolicy.NO_WRITES_COMPACTIONS); 428 } 429 430 @Test 431 public void testSetQuotaAndThenRemoveInOneWithDisable() throws Exception { 432 setQuotaAndThenRemoveInOneAmongTwoTables(SpaceViolationPolicy.DISABLE); 433 } 434 435 @Test 436 public void testSetQuotaOnNonExistingTableWithNoInserts() throws Exception { 437 setQuotaLimit(NON_EXISTENT_TABLE, SpaceViolationPolicy.NO_INSERTS, 2L); 438 } 439 440 @Test 441 public void testSetQuotaOnNonExistingTableWithNoWrites() throws Exception { 442 setQuotaLimit(NON_EXISTENT_TABLE, SpaceViolationPolicy.NO_WRITES, 2L); 443 } 444 445 @Test 446 public void testSetQuotaOnNonExistingTableWithNoWritesCompaction() throws Exception { 447 setQuotaLimit(NON_EXISTENT_TABLE, SpaceViolationPolicy.NO_WRITES_COMPACTIONS, 2L); 448 } 449 450 @Test 451 public void testSetQuotaOnNonExistingTableWithDisable() throws Exception { 452 setQuotaLimit(NON_EXISTENT_TABLE, SpaceViolationPolicy.DISABLE, 2L); 453 } 454 455 private void setQuotaAndThenRemove(SpaceViolationPolicy policy) throws Exception { 456 Put put = new Put(Bytes.toBytes("to_reject")); 457 put.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), 458 Bytes.toBytes("reject")); 459 460 // Do puts until we violate space policy 461 final TableName tn = writeUntilViolationAndVerifyViolation(policy, put); 462 463 // Now, remove the quota 464 removeQuotaFromtable(tn); 465 466 // Put some rows now: should not violate as quota settings removed 467 verifyNoViolation(policy, tn, put); 468 } 469 470 private void setQuotaAndThenDropTable(SpaceViolationPolicy policy) throws Exception { 471 Put put = new Put(Bytes.toBytes("to_reject")); 472 put.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), 473 Bytes.toBytes("reject")); 474 475 // Do puts until we violate space policy 476 final TableName tn = writeUntilViolationAndVerifyViolation(policy, put); 477 478 // Now, drop the table 479 TEST_UTIL.deleteTable(tn); 480 LOG.debug("Successfully deleted table ", tn); 481 482 // Now re-create the table 483 TEST_UTIL.createTable(tn, Bytes.toBytes(SpaceQuotaHelperForTests.F1)); 484 LOG.debug("Successfully re-created table ", tn); 485 486 // Put some rows now: should not violate as table/quota was dropped 487 verifyNoViolation(policy, tn, put); 488 } 489 490 private void setQuotaAndThenIncreaseQuota(SpaceViolationPolicy policy) throws Exception { 491 Put put = new Put(Bytes.toBytes("to_reject")); 492 put.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), 493 Bytes.toBytes("reject")); 494 495 // Do puts until we violate space policy 496 final TableName tn = writeUntilViolationAndVerifyViolation(policy, put); 497 498 // Now, increase limit and perform put 499 setQuotaLimit(tn, policy, 4L); 500 501 // Put some row now: should not violate as quota limit increased 502 verifyNoViolation(policy, tn, put); 503 } 504 505 public void setQuotaAndThenRemoveInOneAmongTwoTables(SpaceViolationPolicy policy) 506 throws Exception { 507 Put put = new Put(Bytes.toBytes("to_reject")); 508 put.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), 509 Bytes.toBytes("reject")); 510 511 // Do puts until we violate space policy on table tn1 512 final TableName tn1 = writeUntilViolationAndVerifyViolation(policy, put); 513 514 // Do puts until we violate space policy on table tn2 515 final TableName tn2 = writeUntilViolationAndVerifyViolation(policy, put); 516 517 // Now, remove the quota from table tn1 518 removeQuotaFromtable(tn1); 519 520 // Put a new row now on tn1: should not violate as quota settings removed 521 verifyNoViolation(policy, tn1, put); 522 // Put a new row now on tn2: should violate as quota settings exists 523 verifyViolation(policy, tn2, put); 524 } 525 526 private void removeQuotaFromtable(final TableName tn) throws Exception { 527 QuotaSettings removeQuota = QuotaSettingsFactory.removeTableSpaceLimit(tn); 528 TEST_UTIL.getAdmin().setQuota(removeQuota); 529 LOG.debug("Space quota settings removed from the table ", tn); 530 } 531 532 private void setQuotaLimit(final TableName tn, SpaceViolationPolicy policy, long sizeInMBs) 533 throws Exception { 534 final long sizeLimit = sizeInMBs * SpaceQuotaHelperForTests.ONE_MEGABYTE; 535 QuotaSettings settings = QuotaSettingsFactory.limitTableSpace(tn, sizeLimit, policy); 536 TEST_UTIL.getAdmin().setQuota(settings); 537 LOG.debug("Quota limit set for table = {}, limit = {}", tn, sizeLimit); 538 } 539 540 private Map<RegionInfo,Long> getReportedSizesForTable(TableName tn) { 541 HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster(); 542 MasterQuotaManager quotaManager = master.getMasterQuotaManager(); 543 Map<RegionInfo,Long> filteredRegionSizes = new HashMap<>(); 544 for (Entry<RegionInfo,Long> entry : quotaManager.snapshotRegionSizes().entrySet()) { 545 if (entry.getKey().getTable().equals(tn)) { 546 filteredRegionSizes.put(entry.getKey(), entry.getValue()); 547 } 548 } 549 return filteredRegionSizes; 550 } 551 552 private TableName writeUntilViolation(SpaceViolationPolicy policyToViolate) throws Exception { 553 TableName tn = helper.createTableWithRegions(10); 554 setQuotaLimit(tn, policyToViolate, 2L); 555 // Write more data than should be allowed and flush it to disk 556 helper.writeData(tn, 3L * SpaceQuotaHelperForTests.ONE_MEGABYTE); 557 558 // This should be sufficient time for the chores to run and see the change. 559 Thread.sleep(5000); 560 561 return tn; 562 } 563 564 private TableName writeUntilViolationAndVerifyViolation( 565 SpaceViolationPolicy policyToViolate, Mutation m) throws Exception { 566 final TableName tn = writeUntilViolation(policyToViolate); 567 verifyViolation(policyToViolate, tn, m); 568 return tn; 569 } 570 571 private void verifyViolation( 572 SpaceViolationPolicy policyToViolate, TableName tn, Mutation m) throws Exception { 573 // But let's try a few times to get the exception before failing 574 boolean sawError = false; 575 for (int i = 0; i < NUM_RETRIES && !sawError; i++) { 576 try (Table table = TEST_UTIL.getConnection().getTable(tn)) { 577 if (m instanceof Put) { 578 table.put((Put) m); 579 } else if (m instanceof Delete) { 580 table.delete((Delete) m); 581 } else if (m instanceof Append) { 582 table.append((Append) m); 583 } else if (m instanceof Increment) { 584 table.increment((Increment) m); 585 } else { 586 fail( 587 "Failed to apply " + m.getClass().getSimpleName() + 588 " to the table. Programming error"); 589 } 590 LOG.info("Did not reject the " + m.getClass().getSimpleName() + ", will sleep and retry"); 591 Thread.sleep(2000); 592 } catch (Exception e) { 593 String msg = StringUtils.stringifyException(e); 594 if (policyToViolate.equals(SpaceViolationPolicy.DISABLE)) { 595 assertTrue(e instanceof TableNotEnabledException); 596 } else { 597 assertTrue("Expected exception message to contain the word '" + policyToViolate.name() 598 + "', but was " + msg, 599 msg.contains(policyToViolate.name())); 600 } 601 sawError = true; 602 } 603 } 604 if (!sawError) { 605 try (Table quotaTable = TEST_UTIL.getConnection().getTable(QuotaUtil.QUOTA_TABLE_NAME)) { 606 ResultScanner scanner = quotaTable.getScanner(new Scan()); 607 Result result = null; 608 LOG.info("Dumping contents of hbase:quota table"); 609 while ((result = scanner.next()) != null) { 610 LOG.info(Bytes.toString(result.getRow()) + " => " + result.toString()); 611 } 612 scanner.close(); 613 } 614 } 615 assertTrue( 616 "Expected to see an exception writing data to a table exceeding its quota", sawError); 617 } 618 619 private ClientServiceCallable<Void> generateFileToLoad( 620 TableName tn, int numFiles, int numRowsPerFile) throws Exception { 621 Connection conn = TEST_UTIL.getConnection(); 622 FileSystem fs = TEST_UTIL.getTestFileSystem(); 623 Configuration conf = TEST_UTIL.getConfiguration(); 624 Path baseDir = new Path(fs.getHomeDirectory(), testName.getMethodName() + "_files"); 625 fs.mkdirs(baseDir); 626 final List<Pair<byte[], String>> famPaths = new ArrayList<Pair<byte[], String>>(); 627 for (int i = 1; i <= numFiles; i++) { 628 Path hfile = new Path(baseDir, "file" + i); 629 TestHRegionServerBulkLoad.createHFile( 630 fs, hfile, Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), 631 Bytes.toBytes("reject"), numRowsPerFile); 632 famPaths.add(new Pair<>(Bytes.toBytes(SpaceQuotaHelperForTests.F1), hfile.toString())); 633 } 634 635 // bulk load HFiles 636 Table table = conn.getTable(tn); 637 final String bulkToken = new SecureBulkLoadClient(conf, table).prepareBulkLoad(conn); 638 return new ClientServiceCallable<Void>(conn, 639 tn, Bytes.toBytes("row"), new RpcControllerFactory(conf).newController(), HConstants.PRIORITY_UNSET) { 640 @Override 641 public Void rpcCall() throws Exception { 642 SecureBulkLoadClient secureClient = null; 643 byte[] regionName = getLocation().getRegionInfo().getRegionName(); 644 try (Table table = conn.getTable(getTableName())) { 645 secureClient = new SecureBulkLoadClient(conf, table); 646 secureClient.secureBulkLoadHFiles(getStub(), famPaths, regionName, 647 true, null, bulkToken); 648 } 649 return null; 650 } 651 }; 652 } 653 654 private void verifyNoViolation(SpaceViolationPolicy policyToViolate, TableName tn, Mutation m) 655 throws Exception { 656 // But let's try a few times to write data before failing 657 boolean sawSuccess = false; 658 for (int i = 0; i < NUM_RETRIES && !sawSuccess; i++) { 659 try (Table table = TEST_UTIL.getConnection().getTable(tn)) { 660 if (m instanceof Put) { 661 table.put((Put) m); 662 } else if (m instanceof Delete) { 663 table.delete((Delete) m); 664 } else if (m instanceof Append) { 665 table.append((Append) m); 666 } else if (m instanceof Increment) { 667 table.increment((Increment) m); 668 } else { 669 fail( 670 "Failed to apply " + m.getClass().getSimpleName() + " to the table. Programming error"); 671 } 672 sawSuccess = true; 673 } catch (Exception e) { 674 LOG.info("Rejected the " + m.getClass().getSimpleName() + ", will sleep and retry"); 675 Thread.sleep(2000); 676 } 677 } 678 if (!sawSuccess) { 679 try (Table quotaTable = TEST_UTIL.getConnection().getTable(QuotaUtil.QUOTA_TABLE_NAME)) { 680 ResultScanner scanner = quotaTable.getScanner(new Scan()); 681 Result result = null; 682 LOG.info("Dumping contents of hbase:quota table"); 683 while ((result = scanner.next()) != null) { 684 LOG.info(Bytes.toString(result.getRow()) + " => " + result.toString()); 685 } 686 scanner.close(); 687 } 688 } 689 assertTrue("Expected to succeed in writing data to a table not having quota ", sawSuccess); 690 } 691}