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 * http://www.apache.org/licenses/LICENSE-2.0 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.apache.hadoop.hbase.quotas; 017 018import static org.junit.Assert.assertEquals; 019import static org.junit.Assert.assertNull; 020import static org.junit.Assert.assertTrue; 021import static org.junit.Assert.fail; 022 023import java.util.HashMap; 024import java.util.Map; 025import java.util.concurrent.atomic.AtomicLong; 026 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.fs.FileStatus; 029import org.apache.hadoop.fs.FileSystem; 030import org.apache.hadoop.fs.Path; 031import org.apache.hadoop.hbase.HBaseClassTestRule; 032import org.apache.hadoop.hbase.HBaseTestingUtility; 033import org.apache.hadoop.hbase.TableName; 034import org.apache.hadoop.hbase.client.ClientServiceCallable; 035import org.apache.hadoop.hbase.client.Put; 036import org.apache.hadoop.hbase.client.RegionInfo; 037import org.apache.hadoop.hbase.client.ResultScanner; 038import org.apache.hadoop.hbase.client.RpcRetryingCaller; 039import org.apache.hadoop.hbase.client.RpcRetryingCallerFactory; 040import org.apache.hadoop.hbase.client.Scan; 041import org.apache.hadoop.hbase.client.Table; 042import org.apache.hadoop.hbase.master.HMaster; 043import org.apache.hadoop.hbase.quotas.policies.DefaultViolationPolicyEnforcement; 044import org.apache.hadoop.hbase.regionserver.HRegionServer; 045import org.apache.hadoop.hbase.testclassification.MediumTests; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.junit.AfterClass; 048import org.junit.Before; 049import org.junit.BeforeClass; 050import org.junit.ClassRule; 051import org.junit.Rule; 052import org.junit.Test; 053import org.junit.experimental.categories.Category; 054import org.junit.rules.TestName; 055import org.slf4j.Logger; 056import org.slf4j.LoggerFactory; 057 058@Category(MediumTests.class) 059public class TestSpaceQuotaOnBulkLoad { 060 061 @ClassRule 062 public static final HBaseClassTestRule CLASS_RULE = 063 HBaseClassTestRule.forClass(TestSpaceQuotaOnBulkLoad.class); 064 065 private static final Logger LOG = LoggerFactory.getLogger(TestSpaceQuotaOnBulkLoad.class); 066 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 067 068 @Rule 069 public TestName testName = new TestName(); 070 private SpaceQuotaHelperForTests helper; 071 072 @BeforeClass 073 public static void setUp() throws Exception { 074 Configuration conf = TEST_UTIL.getConfiguration(); 075 SpaceQuotaHelperForTests.updateConfigForQuotas(conf); 076 TEST_UTIL.startMiniCluster(1); 077 } 078 079 @AfterClass 080 public static void tearDown() throws Exception { 081 TEST_UTIL.shutdownMiniCluster(); 082 } 083 084 @Before 085 public void removeAllQuotas() throws Exception { 086 helper = new SpaceQuotaHelperForTests(TEST_UTIL, testName, new AtomicLong(0)); 087 helper.removeAllQuotas(); 088 } 089 090 @Test 091 public void testNoBulkLoadsWithNoWrites() throws Exception { 092 Put p = new Put(Bytes.toBytes("to_reject")); 093 p.addColumn(Bytes.toBytes(SpaceQuotaHelperForTests.F1), Bytes.toBytes("to"), 094 Bytes.toBytes("reject")); 095 TableName tableName = 096 helper.writeUntilViolationAndVerifyViolation(SpaceViolationPolicy.NO_WRITES, p); 097 098 // The table is now in violation. Try to do a bulk load 099 ClientServiceCallable<Void> callable = helper.generateFileToLoad(tableName, 1, 50); 100 RpcRetryingCallerFactory factory = new RpcRetryingCallerFactory(TEST_UTIL.getConfiguration()); 101 RpcRetryingCaller<Void> caller = factory.<Void> newCaller(); 102 try { 103 caller.callWithRetries(callable, Integer.MAX_VALUE); 104 fail("Expected the bulk load call to fail!"); 105 } catch (SpaceLimitingException e) { 106 // Pass 107 LOG.trace("Caught expected exception", e); 108 } 109 } 110 111 @Test 112 public void testAtomicBulkLoadUnderQuota() throws Exception { 113 // Need to verify that if the batch of hfiles cannot be loaded, none are loaded. 114 TableName tn = helper.createTableWithRegions(10); 115 116 final long sizeLimit = 50L * SpaceQuotaHelperForTests.ONE_KILOBYTE; 117 QuotaSettings settings = 118 QuotaSettingsFactory.limitTableSpace(tn, sizeLimit, SpaceViolationPolicy.NO_INSERTS); 119 TEST_UTIL.getAdmin().setQuota(settings); 120 121 HRegionServer rs = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0); 122 RegionServerSpaceQuotaManager spaceQuotaManager = rs.getRegionServerSpaceQuotaManager(); 123 Map<TableName, SpaceQuotaSnapshot> snapshots = spaceQuotaManager.copyQuotaSnapshots(); 124 Map<RegionInfo, Long> regionSizes = getReportedSizesForTable(tn); 125 while (true) { 126 SpaceQuotaSnapshot snapshot = snapshots.get(tn); 127 if (snapshot != null && snapshot.getLimit() > 0) { 128 break; 129 } 130 LOG.debug("Snapshot does not yet realize quota limit: " + snapshots + ", regionsizes: " 131 + regionSizes); 132 Thread.sleep(3000); 133 snapshots = spaceQuotaManager.copyQuotaSnapshots(); 134 regionSizes = getReportedSizesForTable(tn); 135 } 136 // Our quota limit should be reflected in the latest snapshot 137 SpaceQuotaSnapshot snapshot = snapshots.get(tn); 138 assertEquals(0L, snapshot.getUsage()); 139 assertEquals(sizeLimit, snapshot.getLimit()); 140 141 // We would also not have a "real" policy in violation 142 ActivePolicyEnforcement activePolicies = spaceQuotaManager.getActiveEnforcements(); 143 SpaceViolationPolicyEnforcement enforcement = activePolicies.getPolicyEnforcement(tn); 144 assertTrue("Expected to find Noop policy, but got " + enforcement.getClass().getSimpleName(), 145 enforcement instanceof DefaultViolationPolicyEnforcement); 146 147 // Should generate two files, each of which is over 25KB each 148 ClientServiceCallable<Void> callable = helper.generateFileToLoad(tn, 2, 500); 149 FileSystem fs = TEST_UTIL.getTestFileSystem(); 150 FileStatus[] files = 151 fs.listStatus(new Path(fs.getHomeDirectory(), testName.getMethodName() + "_files")); 152 for (FileStatus file : files) { 153 assertTrue( 154 "Expected the file, " + file.getPath() + ", length to be larger than 25KB, but was " 155 + file.getLen(), file.getLen() > 25 * SpaceQuotaHelperForTests.ONE_KILOBYTE); 156 LOG.debug(file.getPath() + " -> " + file.getLen() + "B"); 157 } 158 159 RpcRetryingCallerFactory factory = new RpcRetryingCallerFactory(TEST_UTIL.getConfiguration()); 160 RpcRetryingCaller<Void> caller = factory.<Void>newCaller(); 161 try { 162 caller.callWithRetries(callable, Integer.MAX_VALUE); 163 fail("Expected the bulk load call to fail!"); 164 } catch (SpaceLimitingException e) { 165 // Pass 166 LOG.trace("Caught expected exception", e); 167 } 168 // Verify that we have no data in the table because neither file should have been 169 // loaded even though one of the files could have. 170 Table table = TEST_UTIL.getConnection().getTable(tn); 171 ResultScanner scanner = table.getScanner(new Scan()); 172 try { 173 assertNull("Expected no results", scanner.next()); 174 } finally { 175 scanner.close(); 176 } 177 } 178 179 private Map<RegionInfo, Long> getReportedSizesForTable(TableName tn) { 180 HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster(); 181 MasterQuotaManager quotaManager = master.getMasterQuotaManager(); 182 Map<RegionInfo, Long> filteredRegionSizes = new HashMap<>(); 183 for (Map.Entry<RegionInfo, Long> entry : quotaManager.snapshotRegionSizes().entrySet()) { 184 if (entry.getKey().getTable().equals(tn)) { 185 filteredRegionSizes.put(entry.getKey(), entry.getValue()); 186 } 187 } 188 return filteredRegionSizes; 189 } 190}