Skip to content

Commit 9305086

Browse files
author
Ray Mattingly
committed
HBASE-29663 TimeBasedLimiters should support dynamic configuration refresh
1 parent e575525 commit 9305086

File tree

11 files changed

+91
-69
lines changed

11 files changed

+91
-69
lines changed

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
2121
import org.apache.yetus.audience.InterfaceAudience;
2222
import org.apache.yetus.audience.InterfaceStability;
23-
24-
import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
23+
import org.slf4j.Logger;
24+
import org.slf4j.LoggerFactory;
2525

2626
/**
2727
* With this limiter resources will be refilled only after a fixed interval of time.
@@ -43,6 +43,8 @@ public class FixedIntervalRateLimiter extends RateLimiter {
4343
public static final String RATE_LIMITER_REFILL_INTERVAL_MS =
4444
"hbase.quota.rate.limiter.refill.interval.ms";
4545

46+
private static final Logger LOG = LoggerFactory.getLogger(FixedIntervalRateLimiter.class);
47+
4648
private long nextRefillTime = -1L;
4749
private final long refillInterval;
4850

@@ -52,10 +54,14 @@ public FixedIntervalRateLimiter() {
5254

5355
public FixedIntervalRateLimiter(long refillInterval) {
5456
super();
55-
Preconditions.checkArgument(getTimeUnitInMillis() >= refillInterval,
56-
String.format("Refill interval %s must be less than or equal to TimeUnit millis %s",
57-
refillInterval, getTimeUnitInMillis()));
58-
this.refillInterval = refillInterval;
57+
long timeUnit = getTimeUnitInMillis();
58+
if (refillInterval > timeUnit) {
59+
LOG.warn(
60+
"Refill interval {} is larger than time unit {}. This is invalid. "
61+
+ "Instead, we will use the time unit {} as the refill interval",
62+
refillInterval, timeUnit, timeUnit);
63+
}
64+
this.refillInterval = Math.min(timeUnit, refillInterval);
5965
}
6066

6167
@Override

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,20 +130,23 @@ private void ensureInitialized() {
130130
}
131131

132132
private Map<String, UserQuotaState> fetchUserQuotaStateEntries() throws IOException {
133-
return QuotaUtil.fetchUserQuotas(rsServices.getConnection(), tableMachineQuotaFactors,
134-
machineQuotaFactor);
133+
return QuotaUtil.fetchUserQuotas(rsServices.getConfiguration(), rsServices.getConnection(),
134+
tableMachineQuotaFactors, machineQuotaFactor);
135135
}
136136

137137
private Map<String, QuotaState> fetchRegionServerQuotaStateEntries() throws IOException {
138-
return QuotaUtil.fetchRegionServerQuotas(rsServices.getConnection());
138+
return QuotaUtil.fetchRegionServerQuotas(rsServices.getConfiguration(),
139+
rsServices.getConnection());
139140
}
140141

141142
private Map<TableName, QuotaState> fetchTableQuotaStateEntries() throws IOException {
142-
return QuotaUtil.fetchTableQuotas(rsServices.getConnection(), tableMachineQuotaFactors);
143+
return QuotaUtil.fetchTableQuotas(rsServices.getConfiguration(), rsServices.getConnection(),
144+
tableMachineQuotaFactors);
143145
}
144146

145147
private Map<String, QuotaState> fetchNamespaceQuotaStateEntries() throws IOException {
146-
return QuotaUtil.fetchNamespaceQuotas(rsServices.getConnection(), machineQuotaFactor);
148+
return QuotaUtil.fetchNamespaceQuotas(rsServices.getConfiguration(), rsServices.getConnection(),
149+
machineQuotaFactor);
147150
}
148151

149152
/**

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
package org.apache.hadoop.hbase.quotas;
1919

20+
import org.apache.hadoop.conf.Configuration;
2021
import org.apache.yetus.audience.InterfaceAudience;
2122
import org.apache.yetus.audience.InterfaceStability;
2223

@@ -25,8 +26,8 @@
2526
@InterfaceAudience.Private
2627
@InterfaceStability.Evolving
2728
public class QuotaLimiterFactory {
28-
public static QuotaLimiter fromThrottle(final Throttle throttle) {
29-
return TimeBasedLimiter.fromThrottle(throttle);
29+
public static QuotaLimiter fromThrottle(Configuration conf, final Throttle throttle) {
30+
return TimeBasedLimiter.fromThrottle(conf, throttle);
3031
}
3132

3233
public static QuotaLimiter update(final QuotaLimiter a, final QuotaLimiter b) {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
package org.apache.hadoop.hbase.quotas;
1919

20+
import org.apache.hadoop.conf.Configuration;
2021
import org.apache.yetus.audience.InterfaceAudience;
2122
import org.apache.yetus.audience.InterfaceStability;
2223

@@ -57,9 +58,9 @@ public synchronized boolean isBypass() {
5758
/**
5859
* Setup the global quota information. (This operation is part of the QuotaState setup)
5960
*/
60-
public synchronized void setQuotas(final Quotas quotas) {
61+
public synchronized void setQuotas(Configuration conf, final Quotas quotas) {
6162
if (quotas.hasThrottle()) {
62-
globalLimiter = QuotaLimiterFactory.fromThrottle(quotas.getThrottle());
63+
globalLimiter = QuotaLimiterFactory.fromThrottle(conf, quotas.getThrottle());
6364
} else {
6465
globalLimiter = NoopQuotaLimiter.get();
6566
}

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

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,9 @@ private static void deleteQuotas(final Connection connection, final byte[] rowKe
330330
doDelete(connection, delete);
331331
}
332332

333-
public static Map<String, UserQuotaState> fetchUserQuotas(final Connection connection,
334-
Map<TableName, Double> tableMachineQuotaFactors, double factor) throws IOException {
333+
public static Map<String, UserQuotaState> fetchUserQuotas(final Configuration conf,
334+
final Connection connection, Map<TableName, Double> tableMachineQuotaFactors, double factor)
335+
throws IOException {
335336
Map<String, UserQuotaState> userQuotas = new HashMap<>();
336337
try (Table table = connection.getTable(QUOTA_TABLE_NAME)) {
337338
Scan scan = new Scan();
@@ -351,7 +352,7 @@ public static Map<String, UserQuotaState> fetchUserQuotas(final Connection conne
351352
@Override
352353
public void visitUserQuotas(String userName, String namespace, Quotas quotas) {
353354
quotas = updateClusterQuotaToMachineQuota(quotas, factor);
354-
quotaInfo.setQuotas(namespace, quotas);
355+
quotaInfo.setQuotas(conf, namespace, quotas);
355356
}
356357

357358
@Override
@@ -360,13 +361,13 @@ public void visitUserQuotas(String userName, TableName table, Quotas quotas) {
360361
tableMachineQuotaFactors.containsKey(table)
361362
? tableMachineQuotaFactors.get(table)
362363
: 1);
363-
quotaInfo.setQuotas(table, quotas);
364+
quotaInfo.setQuotas(conf, table, quotas);
364365
}
365366

366367
@Override
367368
public void visitUserQuotas(String userName, Quotas quotas) {
368369
quotas = updateClusterQuotaToMachineQuota(quotas, factor);
369-
quotaInfo.setQuotas(quotas);
370+
quotaInfo.setQuotas(conf, quotas);
370371
}
371372
});
372373
} catch (IOException e) {
@@ -407,7 +408,7 @@ protected static UserQuotaState buildDefaultUserQuotaState(Configuration conf) {
407408
UserQuotaState state = new UserQuotaState();
408409
QuotaProtos.Quotas defaultQuotas =
409410
QuotaProtos.Quotas.newBuilder().setThrottle(throttleBuilder.build()).build();
410-
state.setQuotas(defaultQuotas);
411+
state.setQuotas(conf, defaultQuotas);
411412
return state;
412413
}
413414

@@ -420,12 +421,12 @@ private static Optional<TimedQuota> buildDefaultTimedQuota(Configuration conf, S
420421
java.util.concurrent.TimeUnit.SECONDS, org.apache.hadoop.hbase.quotas.QuotaScope.MACHINE));
421422
}
422423

423-
public static Map<TableName, QuotaState> fetchTableQuotas(final Connection connection,
424-
Map<TableName, Double> tableMachineFactors) throws IOException {
424+
public static Map<TableName, QuotaState> fetchTableQuotas(final Configuration conf,
425+
final Connection connection, Map<TableName, Double> tableMachineFactors) throws IOException {
425426
Scan scan = new Scan();
426427
scan.addFamily(QUOTA_FAMILY_INFO);
427428
scan.setStartStopRowForPrefixScan(QUOTA_TABLE_ROW_KEY_PREFIX);
428-
return fetchGlobalQuotas("table", scan, connection, new KeyFromRow<TableName>() {
429+
return fetchGlobalQuotas(conf, "table", scan, connection, new KeyFromRow<TableName>() {
429430
@Override
430431
public TableName getKeyFromRow(final byte[] row) {
431432
assert isTableRowKey(row);
@@ -439,12 +440,12 @@ public double getFactor(TableName tableName) {
439440
});
440441
}
441442

442-
public static Map<String, QuotaState> fetchNamespaceQuotas(final Connection connection,
443-
double factor) throws IOException {
443+
public static Map<String, QuotaState> fetchNamespaceQuotas(final Configuration conf,
444+
final Connection connection, double factor) throws IOException {
444445
Scan scan = new Scan();
445446
scan.addFamily(QUOTA_FAMILY_INFO);
446447
scan.setStartStopRowForPrefixScan(QUOTA_NAMESPACE_ROW_KEY_PREFIX);
447-
return fetchGlobalQuotas("namespace", scan, connection, new KeyFromRow<String>() {
448+
return fetchGlobalQuotas(conf, "namespace", scan, connection, new KeyFromRow<String>() {
448449
@Override
449450
public String getKeyFromRow(final byte[] row) {
450451
assert isNamespaceRowKey(row);
@@ -458,12 +459,12 @@ public double getFactor(String s) {
458459
});
459460
}
460461

461-
public static Map<String, QuotaState> fetchRegionServerQuotas(final Connection connection)
462-
throws IOException {
462+
public static Map<String, QuotaState> fetchRegionServerQuotas(final Configuration conf,
463+
final Connection connection) throws IOException {
463464
Scan scan = new Scan();
464465
scan.addFamily(QUOTA_FAMILY_INFO);
465466
scan.setStartStopRowForPrefixScan(QUOTA_REGION_SERVER_ROW_KEY_PREFIX);
466-
return fetchGlobalQuotas("regionServer", scan, connection, new KeyFromRow<String>() {
467+
return fetchGlobalQuotas(conf, "regionServer", scan, connection, new KeyFromRow<String>() {
467468
@Override
468469
public String getKeyFromRow(final byte[] row) {
469470
assert isRegionServerRowKey(row);
@@ -477,8 +478,9 @@ public double getFactor(String s) {
477478
});
478479
}
479480

480-
public static <K> Map<K, QuotaState> fetchGlobalQuotas(final String type, final Scan scan,
481-
final Connection connection, final KeyFromRow<K> kfr) throws IOException {
481+
public static <K> Map<K, QuotaState> fetchGlobalQuotas(final Configuration conf,
482+
final String type, final Scan scan, final Connection connection, final KeyFromRow<K> kfr)
483+
throws IOException {
482484

483485
Map<K, QuotaState> globalQuotas = new HashMap<>();
484486
try (Table table = connection.getTable(QUOTA_TABLE_NAME)) {
@@ -499,7 +501,7 @@ public static <K> Map<K, QuotaState> fetchGlobalQuotas(final String type, final
499501
try {
500502
Quotas quotas = quotasFromData(data);
501503
quotas = updateClusterQuotaToMachineQuota(quotas, kfr.getFactor(key));
502-
quotaInfo.setQuotas(quotas);
504+
quotaInfo.setQuotas(conf, quotas);
503505
} catch (IOException e) {
504506
LOG.error("Unable to parse {} '{}' quotas", type, key, e);
505507
globalQuotas.remove(key);

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
package org.apache.hadoop.hbase.quotas;
1919

2020
import org.apache.hadoop.conf.Configuration;
21-
import org.apache.hadoop.hbase.HBaseConfiguration;
2221
import org.apache.yetus.audience.InterfaceAudience;
2322
import org.apache.yetus.audience.InterfaceStability;
2423

@@ -32,7 +31,6 @@
3231
@InterfaceAudience.Private
3332
@InterfaceStability.Evolving
3433
public class TimeBasedLimiter implements QuotaLimiter {
35-
private static final Configuration conf = HBaseConfiguration.create();
3634
private RateLimiter reqsLimiter = null;
3735
private RateLimiter reqSizeLimiter = null;
3836
private RateLimiter writeReqsLimiter = null;
@@ -47,7 +45,7 @@ public class TimeBasedLimiter implements QuotaLimiter {
4745
private RateLimiter atomicWriteSizeLimiter = null;
4846
private RateLimiter reqHandlerUsageTimeLimiter = null;
4947

50-
private TimeBasedLimiter() {
48+
private TimeBasedLimiter(Configuration conf) {
5149
if (
5250
FixedIntervalRateLimiter.class.getName().equals(
5351
conf.getClass(RateLimiter.QUOTA_RATE_LIMITER_CONF_KEY, AverageIntervalRateLimiter.class)
@@ -85,8 +83,8 @@ private TimeBasedLimiter() {
8583
}
8684
}
8785

88-
static QuotaLimiter fromThrottle(final Throttle throttle) {
89-
TimeBasedLimiter limiter = new TimeBasedLimiter();
86+
static QuotaLimiter fromThrottle(Configuration conf, final Throttle throttle) {
87+
TimeBasedLimiter limiter = new TimeBasedLimiter(conf);
9088
boolean isBypass = true;
9189
if (throttle.hasReqNum()) {
9290
setFromTimedQuota(limiter.reqsLimiter, throttle.getReqNum());

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.HashSet;
2222
import java.util.Map;
2323
import java.util.Set;
24+
import org.apache.hadoop.conf.Configuration;
2425
import org.apache.hadoop.hbase.TableName;
2526
import org.apache.yetus.audience.InterfaceAudience;
2627
import org.apache.yetus.audience.InterfaceStability;
@@ -89,39 +90,39 @@ public synchronized boolean hasBypassGlobals() {
8990
}
9091

9192
@Override
92-
public synchronized void setQuotas(final Quotas quotas) {
93-
super.setQuotas(quotas);
93+
public synchronized void setQuotas(Configuration conf, final Quotas quotas) {
94+
super.setQuotas(conf, quotas);
9495
bypassGlobals = quotas.getBypassGlobals();
9596
}
9697

9798
/**
9899
* Add the quota information of the specified table. (This operation is part of the QuotaState
99100
* setup)
100101
*/
101-
public synchronized void setQuotas(final TableName table, Quotas quotas) {
102-
tableLimiters = setLimiter(tableLimiters, table, quotas);
102+
public synchronized void setQuotas(Configuration conf, final TableName table, Quotas quotas) {
103+
tableLimiters = setLimiter(conf, tableLimiters, table, quotas);
103104
}
104105

105106
/**
106107
* Add the quota information of the specified namespace. (This operation is part of the QuotaState
107108
* setup)
108109
*/
109-
public void setQuotas(final String namespace, Quotas quotas) {
110-
namespaceLimiters = setLimiter(namespaceLimiters, namespace, quotas);
110+
public void setQuotas(Configuration conf, final String namespace, Quotas quotas) {
111+
namespaceLimiters = setLimiter(conf, namespaceLimiters, namespace, quotas);
111112
}
112113

113114
public boolean hasTableLimiters() {
114115
return tableLimiters != null && !tableLimiters.isEmpty();
115116
}
116117

117-
private <K> Map<K, QuotaLimiter> setLimiter(Map<K, QuotaLimiter> limiters, final K key,
118-
final Quotas quotas) {
118+
private <K> Map<K, QuotaLimiter> setLimiter(Configuration conf, Map<K, QuotaLimiter> limiters,
119+
final K key, final Quotas quotas) {
119120
if (limiters == null) {
120121
limiters = new HashMap<>();
121122
}
122123

123124
QuotaLimiter limiter =
124-
quotas.hasThrottle() ? QuotaLimiterFactory.fromThrottle(quotas.getThrottle()) : null;
125+
quotas.hasThrottle() ? QuotaLimiterFactory.fromThrottle(conf, quotas.getThrottle()) : null;
125126
if (limiter != null && !limiter.isBypass()) {
126127
limiters.put(key, limiter);
127128
} else {

hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionCoprocessorQuotaUsage.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public static void setUp() throws Exception {
9393
conf.setBoolean("hbase.quota.enabled", true);
9494
conf.setInt("hbase.quota.default.user.machine.read.num", 2);
9595
conf.set("hbase.quota.rate.limiter", "org.apache.hadoop.hbase.quotas.FixedIntervalRateLimiter");
96-
conf.set("hbase.quota.rate.limiter.refill.interval.ms", "300000");
96+
conf.set("hbase.quota.rate.limiter.refill.interval.ms", "1000");
9797
conf.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, MyCoprocessor.class.getName());
9898
UTIL.startMiniCluster(3);
9999
byte[][] splitKeys = new byte[8][];
@@ -114,7 +114,7 @@ public static void tearDown() throws Exception {
114114
@Test
115115
public void testGet() throws InterruptedException, ExecutionException, IOException {
116116
// Hit the table 5 times which ought to be enough to make a throttle happen
117-
for (int i = 0; i < 5; i++) {
117+
for (int i = 0; i < 50; i++) {
118118
TABLE.get(new Get(Bytes.toBytes("000")));
119119
}
120120
assertTrue("Throttling did not happen as expected", THROTTLING_OCCURRED.get());

0 commit comments

Comments
 (0)