Skip to content

Commit ca4f4bf

Browse files
authored
HubSpot Backport: HBASE-28370 Default user quotas are refreshing too frequently (apache#5686) (#83)
Signed-off-by: Bryan Beaudreault <bbeaudreault@apache.org>
1 parent bfa5f56 commit ca4f4bf

4 files changed

Lines changed: 115 additions & 4 deletions

File tree

hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/QuotaCache.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ public class QuotaCache implements Stoppable {
7070

7171
// for testing purpose only, enforce the cache to be always refreshed
7272
static boolean TEST_FORCE_REFRESH = false;
73+
// for testing purpose only, block cache refreshes to reliably verify state
74+
static boolean TEST_BLOCK_REFRESH = false;
7375

7476
private final ConcurrentHashMap<String, QuotaState> namespaceQuotaCache =
7577
new ConcurrentHashMap<>();
@@ -140,7 +142,7 @@ public QuotaLimiter getUserLimiter(final UserGroupInformation ugi, final TableNa
140142
*/
141143
public UserQuotaState getUserQuotaState(final UserGroupInformation ugi) {
142144
return computeIfAbsent(userQuotaCache, getQuotaUserName(ugi),
143-
() -> QuotaUtil.buildDefaultUserQuotaState(rsServices.getConfiguration()),
145+
() -> QuotaUtil.buildDefaultUserQuotaState(rsServices.getConfiguration(), 0L),
144146
this::triggerCacheRefresh);
145147
}
146148

@@ -242,6 +244,14 @@ public QuotaRefresherChore(final int period, final Stoppable stoppable) {
242244
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "GC_UNRELATED_TYPES",
243245
justification = "I do not understand why the complaints, it looks good to me -- FIX")
244246
protected void chore() {
247+
while (TEST_BLOCK_REFRESH) {
248+
LOG.info("TEST_BLOCK_REFRESH=true, so blocking QuotaCache refresh until it is false");
249+
try {
250+
Thread.sleep(10);
251+
} catch (InterruptedException e) {
252+
throw new RuntimeException(e);
253+
}
254+
}
245255
// Prefetch online tables/namespaces
246256
for (TableName table : ((HRegionServer) QuotaCache.this.rsServices).getOnlineTables()) {
247257
if (table.isSystemTable()) continue;

hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/QuotaUtil.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ public static Map<String, UserQuotaState> fetchUserQuotas(final Connection conne
333333
String user = getUserFromRowKey(key);
334334

335335
if (results[i].isEmpty()) {
336-
userQuotas.put(user, buildDefaultUserQuotaState(connection.getConfiguration()));
336+
userQuotas.put(user, buildDefaultUserQuotaState(connection.getConfiguration(), nowTs));
337337
continue;
338338
}
339339

@@ -373,7 +373,7 @@ public void visitUserQuotas(String userName, Quotas quotas) {
373373
return userQuotas;
374374
}
375375

376-
protected static UserQuotaState buildDefaultUserQuotaState(Configuration conf) {
376+
protected static UserQuotaState buildDefaultUserQuotaState(Configuration conf, long nowTs) {
377377
QuotaProtos.Throttle.Builder throttleBuilder = QuotaProtos.Throttle.newBuilder();
378378

379379
buildDefaultTimedQuota(conf, QUOTA_DEFAULT_USER_MACHINE_READ_NUM)
@@ -389,7 +389,7 @@ protected static UserQuotaState buildDefaultUserQuotaState(Configuration conf) {
389389
buildDefaultTimedQuota(conf, QUOTA_DEFAULT_USER_MACHINE_WRITE_SIZE)
390390
.ifPresent(throttleBuilder::setWriteSize);
391391

392-
UserQuotaState state = new UserQuotaState();
392+
UserQuotaState state = new UserQuotaState(nowTs);
393393
QuotaProtos.Quotas defaultQuotas =
394394
QuotaProtos.Quotas.newBuilder().setThrottle(throttleBuilder.build()).build();
395395
state.setQuotas(defaultQuotas);
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
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
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
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.
17+
*/
18+
package org.apache.hadoop.hbase.quotas;
19+
20+
import static org.apache.hadoop.hbase.quotas.ThrottleQuotaTestUtil.waitMinuteQuota;
21+
import static org.junit.Assert.assertEquals;
22+
23+
import org.apache.hadoop.hbase.HBaseClassTestRule;
24+
import org.apache.hadoop.hbase.HBaseTestingUtility;
25+
import org.apache.hadoop.hbase.testclassification.MediumTests;
26+
import org.apache.hadoop.hbase.testclassification.RegionServerTests;
27+
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
28+
import org.apache.hadoop.security.UserGroupInformation;
29+
import org.junit.After;
30+
import org.junit.BeforeClass;
31+
import org.junit.ClassRule;
32+
import org.junit.Test;
33+
import org.junit.experimental.categories.Category;
34+
35+
@Category({ RegionServerTests.class, MediumTests.class })
36+
public class TestQuotaCache {
37+
38+
@ClassRule
39+
public static final HBaseClassTestRule CLASS_RULE =
40+
HBaseClassTestRule.forClass(TestQuotaCache.class);
41+
42+
private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
43+
private static final int REFRESH_TIME = 30_000;
44+
45+
@After
46+
public void tearDown() throws Exception {
47+
ThrottleQuotaTestUtil.clearQuotaCache(TEST_UTIL);
48+
EnvironmentEdgeManager.reset();
49+
TEST_UTIL.shutdownMiniCluster();
50+
}
51+
52+
@BeforeClass
53+
public static void setUpBeforeClass() throws Exception {
54+
TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, true);
55+
TEST_UTIL.getConfiguration().setInt(QuotaCache.REFRESH_CONF_KEY, REFRESH_TIME);
56+
TEST_UTIL.getConfiguration().setInt(QuotaUtil.QUOTA_DEFAULT_USER_MACHINE_READ_NUM, 1000);
57+
58+
TEST_UTIL.startMiniCluster(1);
59+
TEST_UTIL.waitTableAvailable(QuotaTableUtil.QUOTA_TABLE_NAME);
60+
}
61+
62+
@Test
63+
public void testDefaultUserRefreshFrequency() throws Exception {
64+
QuotaCache.TEST_BLOCK_REFRESH = true;
65+
66+
QuotaCache quotaCache =
67+
ThrottleQuotaTestUtil.getQuotaCaches(TEST_UTIL).stream().findAny().get();
68+
UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
69+
70+
UserQuotaState userQuotaState = quotaCache.getUserQuotaState(ugi);
71+
assertEquals(userQuotaState.getLastUpdate(), 0);
72+
73+
QuotaCache.TEST_BLOCK_REFRESH = false;
74+
// new user should have refreshed immediately
75+
TEST_UTIL.waitFor(5_000, () -> userQuotaState.getLastUpdate() != 0);
76+
long lastUpdate = userQuotaState.getLastUpdate();
77+
78+
// refresh should not apply to recently refreshed quota
79+
quotaCache.triggerCacheRefresh();
80+
Thread.sleep(250);
81+
long newLastUpdate = userQuotaState.getLastUpdate();
82+
assertEquals(lastUpdate, newLastUpdate);
83+
84+
quotaCache.triggerCacheRefresh();
85+
waitMinuteQuota();
86+
// should refresh after time has passed
87+
TEST_UTIL.waitFor(5_000, () -> lastUpdate != userQuotaState.getLastUpdate());
88+
}
89+
}

hbase-server/src/test/java/org/apache/hadoop/hbase/quotas/ThrottleQuotaTestUtil.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919

2020
import java.io.IOException;
2121
import java.util.ArrayList;
22+
import java.util.HashSet;
2223
import java.util.List;
2324
import java.util.Objects;
2425
import java.util.Random;
26+
import java.util.Set;
2527
import org.apache.hadoop.hbase.HBaseTestingUtility;
2628
import org.apache.hadoop.hbase.TableName;
2729
import org.apache.hadoop.hbase.Waiter.ExplainingPredicate;
@@ -282,6 +284,16 @@ public String explainFailure() throws Exception {
282284
}
283285
}
284286

287+
static Set<QuotaCache> getQuotaCaches(HBaseTestingUtility testUtil) {
288+
Set<QuotaCache> quotaCaches = new HashSet<>();
289+
for (RegionServerThread rst : testUtil.getMiniHBaseCluster().getRegionServerThreads()) {
290+
RegionServerRpcQuotaManager quotaManager =
291+
rst.getRegionServer().getRegionServerRpcQuotaManager();
292+
quotaCaches.add(quotaManager.getQuotaCache());
293+
}
294+
return quotaCaches;
295+
}
296+
285297
static void waitMinuteQuota() {
286298
envEdge.incValue(70000);
287299
}

0 commit comments

Comments
 (0)