2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
.quotas
;
20 import static org
.junit
.Assert
.assertEquals
;
21 import static org
.junit
.Assert
.assertFalse
;
22 import static org
.junit
.Assert
.assertNotNull
;
23 import static org
.junit
.Assert
.assertTrue
;
26 import java
.util
.Map
.Entry
;
27 import java
.util
.concurrent
.atomic
.AtomicLong
;
28 import java
.util
.concurrent
.atomic
.AtomicReference
;
29 import org
.apache
.hadoop
.conf
.Configuration
;
30 import org
.apache
.hadoop
.hbase
.HBaseClassTestRule
;
31 import org
.apache
.hadoop
.hbase
.HBaseTestingUtility
;
32 import org
.apache
.hadoop
.hbase
.TableName
;
33 import org
.apache
.hadoop
.hbase
.Waiter
;
34 import org
.apache
.hadoop
.hbase
.Waiter
.Predicate
;
35 import org
.apache
.hadoop
.hbase
.client
.Connection
;
36 import org
.apache
.hadoop
.hbase
.client
.RegionInfo
;
37 import org
.apache
.hadoop
.hbase
.client
.RetriesExhaustedWithDetailsException
;
38 import org
.apache
.hadoop
.hbase
.master
.HMaster
;
39 import org
.apache
.hadoop
.hbase
.quotas
.SpaceQuotaSnapshot
.SpaceQuotaStatus
;
40 import org
.apache
.hadoop
.hbase
.quotas
.policies
.MissingSnapshotViolationPolicyEnforcement
;
41 import org
.apache
.hadoop
.hbase
.regionserver
.HRegionServer
;
42 import org
.apache
.hadoop
.hbase
.testclassification
.MediumTests
;
43 import org
.junit
.AfterClass
;
44 import org
.junit
.Before
;
45 import org
.junit
.BeforeClass
;
46 import org
.junit
.ClassRule
;
47 import org
.junit
.Rule
;
48 import org
.junit
.Test
;
49 import org
.junit
.experimental
.categories
.Category
;
50 import org
.junit
.rules
.TestName
;
51 import org
.slf4j
.Logger
;
52 import org
.slf4j
.LoggerFactory
;
55 * Test class for the quota status RPCs in the master and regionserver.
57 @Category({MediumTests
.class})
58 public class TestQuotaStatusRPCs
{
61 public static final HBaseClassTestRule CLASS_RULE
=
62 HBaseClassTestRule
.forClass(TestQuotaStatusRPCs
.class);
64 private static final Logger LOG
= LoggerFactory
.getLogger(TestQuotaStatusRPCs
.class);
65 private static final HBaseTestingUtility TEST_UTIL
= new HBaseTestingUtility();
66 private static final AtomicLong COUNTER
= new AtomicLong(0);
69 public TestName testName
= new TestName();
70 private SpaceQuotaHelperForTests helper
;
73 public static void setUp() throws Exception
{
74 Configuration conf
= TEST_UTIL
.getConfiguration();
75 // Increase the frequency of some of the chores for responsiveness of the test
76 SpaceQuotaHelperForTests
.updateConfigForQuotas(conf
);
77 TEST_UTIL
.startMiniCluster(1);
81 public static void tearDown() throws Exception
{
82 TEST_UTIL
.shutdownMiniCluster();
86 public void setupForTest() throws Exception
{
87 helper
= new SpaceQuotaHelperForTests(TEST_UTIL
, testName
, COUNTER
);
91 public void testRegionSizesFromMaster() throws Exception
{
92 final long tableSize
= 1024L * 10L; // 10KB
93 final int numRegions
= 10;
94 final TableName tn
= helper
.createTableWithRegions(numRegions
);
95 // Will write at least `tableSize` data
96 helper
.writeData(tn
, tableSize
);
98 final HMaster master
= TEST_UTIL
.getMiniHBaseCluster().getMaster();
99 final MasterQuotaManager quotaManager
= master
.getMasterQuotaManager();
100 // Make sure the master has all of the reports
101 Waiter
.waitFor(TEST_UTIL
.getConfiguration(), 30 * 1000, new Predicate
<Exception
>() {
103 public boolean evaluate() throws Exception
{
104 Map
<RegionInfo
,Long
> regionSizes
= quotaManager
.snapshotRegionSizes();
105 LOG
.trace("Region sizes=" + regionSizes
);
106 return numRegions
== countRegionsForTable(tn
, regionSizes
) &&
107 tableSize
<= getTableSize(tn
, regionSizes
);
111 Map
<TableName
, Long
> sizes
= TEST_UTIL
.getAdmin().getSpaceQuotaTableSizes();
112 Long size
= sizes
.get(tn
);
113 assertNotNull("No reported size for " + tn
, size
);
114 assertTrue("Reported table size was " + size
, size
.longValue() >= tableSize
);
118 public void testQuotaSnapshotsFromRS() throws Exception
{
119 final long sizeLimit
= 1024L * 1024L; // 1MB
120 final long tableSize
= 1024L * 10L; // 10KB
121 final int numRegions
= 10;
122 final TableName tn
= helper
.createTableWithRegions(numRegions
);
125 QuotaSettings settings
= QuotaSettingsFactory
.limitTableSpace(
126 tn
, sizeLimit
, SpaceViolationPolicy
.NO_INSERTS
);
127 TEST_UTIL
.getAdmin().setQuota(settings
);
129 // Write at least `tableSize` data
130 helper
.writeData(tn
, tableSize
);
132 final HRegionServer rs
= TEST_UTIL
.getMiniHBaseCluster().getRegionServer(0);
133 final RegionServerSpaceQuotaManager manager
= rs
.getRegionServerSpaceQuotaManager();
134 Waiter
.waitFor(TEST_UTIL
.getConfiguration(), 30 * 1000, new Predicate
<Exception
>() {
136 public boolean evaluate() throws Exception
{
137 SpaceQuotaSnapshot snapshot
= manager
.copyQuotaSnapshots().get(tn
);
138 if (snapshot
== null) {
141 return snapshot
.getUsage() >= tableSize
;
145 @SuppressWarnings("unchecked")
146 Map
<TableName
, SpaceQuotaSnapshot
> snapshots
= (Map
<TableName
, SpaceQuotaSnapshot
>) TEST_UTIL
147 .getAdmin().getRegionServerSpaceQuotaSnapshots(rs
.getServerName());
148 SpaceQuotaSnapshot snapshot
= snapshots
.get(tn
);
149 assertNotNull("Did not find snapshot for " + tn
, snapshot
);
151 "Observed table usage was " + snapshot
.getUsage(),
152 snapshot
.getUsage() >= tableSize
);
153 assertEquals(sizeLimit
, snapshot
.getLimit());
154 SpaceQuotaStatus pbStatus
= snapshot
.getQuotaStatus();
155 assertFalse(pbStatus
.isInViolation());
159 public void testQuotaEnforcementsFromRS() throws Exception
{
160 final long sizeLimit
= 1024L * 8L; // 8KB
161 final long tableSize
= 1024L * 10L; // 10KB
162 final int numRegions
= 10;
163 final TableName tn
= helper
.createTableWithRegions(numRegions
);
166 QuotaSettings settings
= QuotaSettingsFactory
.limitTableSpace(
167 tn
, sizeLimit
, SpaceViolationPolicy
.NO_INSERTS
);
168 TEST_UTIL
.getAdmin().setQuota(settings
);
170 // Write at least `tableSize` data
172 helper
.writeData(tn
, tableSize
);
173 } catch (RetriesExhaustedWithDetailsException
| SpaceLimitingException e
) {
177 final HRegionServer rs
= TEST_UTIL
.getMiniHBaseCluster().getRegionServer(0);
178 final RegionServerSpaceQuotaManager manager
= rs
.getRegionServerSpaceQuotaManager();
179 Waiter
.waitFor(TEST_UTIL
.getConfiguration(), 30 * 1000, new Predicate
<Exception
>() {
181 public boolean evaluate() throws Exception
{
182 ActivePolicyEnforcement enforcements
= manager
.getActiveEnforcements();
183 SpaceViolationPolicyEnforcement enforcement
= enforcements
.getPolicyEnforcement(tn
);
184 // Signifies that we're waiting on the quota snapshot to be fetched
185 if (enforcement
instanceof MissingSnapshotViolationPolicyEnforcement
) {
188 return enforcement
.getQuotaSnapshot().getQuotaStatus().isInViolation();
192 // We obtain the violations for a RegionServer by observing the snapshots
193 @SuppressWarnings("unchecked")
194 Map
<TableName
, SpaceQuotaSnapshot
> snapshots
= (Map
<TableName
, SpaceQuotaSnapshot
>) TEST_UTIL
195 .getAdmin().getRegionServerSpaceQuotaSnapshots(rs
.getServerName());
196 SpaceQuotaSnapshot snapshot
= snapshots
.get(tn
);
197 assertNotNull("Did not find snapshot for " + tn
, snapshot
);
198 assertTrue(snapshot
.getQuotaStatus().isInViolation());
199 assertEquals(SpaceViolationPolicy
.NO_INSERTS
, snapshot
.getQuotaStatus().getPolicy().get());
203 public void testQuotaStatusFromMaster() throws Exception
{
204 final long sizeLimit
= 1024L * 25L; // 25KB
205 // As of 2.0.0-beta-2, this 1KB of "Cells" actually results in about 15KB on disk (HFiles)
206 // This is skewed a bit since we're writing such little data, so the test needs to keep
207 // this in mind; else, the quota will be in violation before the test expects it to be.
208 final long tableSize
= 1024L * 1; // 1KB
209 final long nsLimit
= Long
.MAX_VALUE
;
210 final int numRegions
= 10;
211 final TableName tn
= helper
.createTableWithRegions(numRegions
);
214 QuotaSettings settings
= QuotaSettingsFactory
.limitTableSpace(
215 tn
, sizeLimit
, SpaceViolationPolicy
.NO_INSERTS
);
216 TEST_UTIL
.getAdmin().setQuota(settings
);
217 QuotaSettings nsSettings
= QuotaSettingsFactory
.limitNamespaceSpace(
218 tn
.getNamespaceAsString(), nsLimit
, SpaceViolationPolicy
.NO_INSERTS
);
219 TEST_UTIL
.getAdmin().setQuota(nsSettings
);
221 // Write at least `tableSize` data
222 helper
.writeData(tn
, tableSize
);
224 final Connection conn
= TEST_UTIL
.getConnection();
225 // Make sure the master has a snapshot for our table
226 Waiter
.waitFor(TEST_UTIL
.getConfiguration(), 30 * 1000, new Predicate
<Exception
>() {
228 public boolean evaluate() throws Exception
{
229 SpaceQuotaSnapshot snapshot
=
230 (SpaceQuotaSnapshot
) conn
.getAdmin().getCurrentSpaceQuotaSnapshot(tn
);
231 LOG
.info("Table snapshot after initial ingest: " + snapshot
);
232 if (snapshot
== null) {
235 return snapshot
.getLimit() == sizeLimit
&& snapshot
.getUsage() > 0L;
238 final AtomicReference
<Long
> nsUsage
= new AtomicReference
<>();
239 // If we saw the table snapshot, we should also see the namespace snapshot
240 Waiter
.waitFor(TEST_UTIL
.getConfiguration(), 30 * 1000 * 1000, new Predicate
<Exception
>() {
242 public boolean evaluate() throws Exception
{
243 SpaceQuotaSnapshot snapshot
= (SpaceQuotaSnapshot
) conn
.getAdmin()
244 .getCurrentSpaceQuotaSnapshot(tn
.getNamespaceAsString());
245 LOG
.debug("Namespace snapshot after initial ingest: " + snapshot
);
246 if (snapshot
== null) {
249 nsUsage
.set(snapshot
.getUsage());
250 return snapshot
.getLimit() == nsLimit
&& snapshot
.getUsage() > 0;
254 // Sanity check: the below assertions will fail if we somehow write too much data
255 // and force the table to move into violation before we write the second bit of data.
256 SpaceQuotaSnapshot snapshot
=
257 (SpaceQuotaSnapshot
) conn
.getAdmin().getCurrentSpaceQuotaSnapshot(tn
);
258 assertTrue("QuotaSnapshot for " + tn
+ " should be non-null and not in violation",
259 snapshot
!= null && !snapshot
.getQuotaStatus().isInViolation());
262 helper
.writeData(tn
, tableSize
* 2L);
263 } catch (RetriesExhaustedWithDetailsException
| SpaceLimitingException e
) {
267 // Wait for the status to move to violation
268 Waiter
.waitFor(TEST_UTIL
.getConfiguration(), 30 * 1000, new Predicate
<Exception
>() {
270 public boolean evaluate() throws Exception
{
271 SpaceQuotaSnapshot snapshot
=
272 (SpaceQuotaSnapshot
) conn
.getAdmin().getCurrentSpaceQuotaSnapshot(tn
);
273 LOG
.info("Table snapshot after second ingest: " + snapshot
);
274 if (snapshot
== null) {
277 return snapshot
.getQuotaStatus().isInViolation();
280 // The namespace should still not be in violation, but have a larger usage than previously
281 Waiter
.waitFor(TEST_UTIL
.getConfiguration(), 30 * 1000, new Predicate
<Exception
>() {
283 public boolean evaluate() throws Exception
{
284 SpaceQuotaSnapshot snapshot
= (SpaceQuotaSnapshot
) conn
.getAdmin()
285 .getCurrentSpaceQuotaSnapshot(tn
.getNamespaceAsString());
286 LOG
.debug("Namespace snapshot after second ingest: " + snapshot
);
287 if (snapshot
== null) {
290 return snapshot
.getUsage() > nsUsage
.get() && !snapshot
.getQuotaStatus().isInViolation();
295 private int countRegionsForTable(TableName tn
, Map
<RegionInfo
,Long
> regionSizes
) {
297 for (RegionInfo regionInfo
: regionSizes
.keySet()) {
298 if (tn
.equals(regionInfo
.getTable())) {
305 private int getTableSize(TableName tn
, Map
<RegionInfo
,Long
> regionSizes
) {
307 for (Entry
<RegionInfo
,Long
> entry
: regionSizes
.entrySet()) {
308 RegionInfo regionInfo
= entry
.getKey();
309 long regionSize
= entry
.getValue();
310 if (tn
.equals(regionInfo
.getTable())) {
311 tableSize
+= regionSize
;