/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.quotas;
import java.util.concurrent.TimeUnit;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas;
import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Throttle;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.apache.hadoop.hbase.testclassification.RegionServerTests;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TestName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@Category({RegionServerTests.class, SmallTests.class})
public class TestQuotaState {
private static final TableName UNKNOWN_TABLE_NAME = TableName.valueOf("unknownTable");
@Rule
public TestName name = new TestName();
@Test(timeout=60000)
public void testQuotaStateBypass() {
QuotaState quotaInfo = new QuotaState();
assertTrue(quotaInfo.isBypass());
assertNoopLimiter(quotaInfo.getGlobalLimiter());
UserQuotaState userQuotaState = new UserQuotaState();
assertTrue(userQuotaState.isBypass());
assertNoopLimiter(userQuotaState.getTableLimiter(UNKNOWN_TABLE_NAME));
}
@Test(timeout=60000)
public void testSimpleQuotaStateOperation() {
final TableName tableName = TableName.valueOf(name.getMethodName());
final int NUM_GLOBAL_THROTTLE = 3;
final int NUM_TABLE_THROTTLE = 2;
UserQuotaState quotaInfo = new UserQuotaState();
assertTrue(quotaInfo.isBypass());
// Set global quota
quotaInfo.setQuotas(buildReqNumThrottle(NUM_GLOBAL_THROTTLE));
assertFalse(quotaInfo.isBypass());
// Set table quota
quotaInfo.setQuotas(tableName, buildReqNumThrottle(NUM_TABLE_THROTTLE));
assertFalse(quotaInfo.isBypass());
assertTrue(quotaInfo.getGlobalLimiter() == quotaInfo.getTableLimiter(UNKNOWN_TABLE_NAME));
assertThrottleException(quotaInfo.getTableLimiter(UNKNOWN_TABLE_NAME), NUM_GLOBAL_THROTTLE);
assertThrottleException(quotaInfo.getTableLimiter(tableName), NUM_TABLE_THROTTLE);
}
@Test(timeout=60000)
public void testQuotaStateUpdateBypassThrottle() {
final long LAST_UPDATE = 10;
UserQuotaState quotaInfo = new UserQuotaState();
assertEquals(0, quotaInfo.getLastUpdate());
assertTrue(quotaInfo.isBypass());
UserQuotaState otherQuotaState = new UserQuotaState(LAST_UPDATE);
assertEquals(LAST_UPDATE, otherQuotaState.getLastUpdate());
assertTrue(otherQuotaState.isBypass());
quotaInfo.update(otherQuotaState);
assertEquals(LAST_UPDATE, quotaInfo.getLastUpdate());
assertTrue(quotaInfo.isBypass());
assertTrue(quotaInfo.getGlobalLimiter() == quotaInfo.getTableLimiter(UNKNOWN_TABLE_NAME));
assertNoopLimiter(quotaInfo.getTableLimiter(UNKNOWN_TABLE_NAME));
}
@Test(timeout=60000)
public void testQuotaStateUpdateGlobalThrottle() {
final int NUM_GLOBAL_THROTTLE_1 = 3;
final int NUM_GLOBAL_THROTTLE_2 = 11;
final long LAST_UPDATE_1 = 10;
final long LAST_UPDATE_2 = 20;
final long LAST_UPDATE_3 = 30;
QuotaState quotaInfo = new QuotaState();
assertEquals(0, quotaInfo.getLastUpdate());
assertTrue(quotaInfo.isBypass());
// Add global throttle
QuotaState otherQuotaState = new QuotaState(LAST_UPDATE_1);
otherQuotaState.setQuotas(buildReqNumThrottle(NUM_GLOBAL_THROTTLE_1));
assertEquals(LAST_UPDATE_1, otherQuotaState.getLastUpdate());
assertFalse(otherQuotaState.isBypass());
quotaInfo.update(otherQuotaState);
assertEquals(LAST_UPDATE_1, quotaInfo.getLastUpdate());
assertFalse(quotaInfo.isBypass());
assertThrottleException(quotaInfo.getGlobalLimiter(), NUM_GLOBAL_THROTTLE_1);
// Update global Throttle
otherQuotaState = new QuotaState(LAST_UPDATE_2);
otherQuotaState.setQuotas(buildReqNumThrottle(NUM_GLOBAL_THROTTLE_2));
assertEquals(LAST_UPDATE_2, otherQuotaState.getLastUpdate());
assertFalse(otherQuotaState.isBypass());
quotaInfo.update(otherQuotaState);
assertEquals(LAST_UPDATE_2, quotaInfo.getLastUpdate());
assertFalse(quotaInfo.isBypass());
assertThrottleException(quotaInfo.getGlobalLimiter(),
NUM_GLOBAL_THROTTLE_2 - NUM_GLOBAL_THROTTLE_1);
// Remove global throttle
otherQuotaState = new QuotaState(LAST_UPDATE_3);
assertEquals(LAST_UPDATE_3, otherQuotaState.getLastUpdate());
assertTrue(otherQuotaState.isBypass());
quotaInfo.update(otherQuotaState);
assertEquals(LAST_UPDATE_3, quotaInfo.getLastUpdate());
assertTrue(quotaInfo.isBypass());
assertNoopLimiter(quotaInfo.getGlobalLimiter());
}
@Test(timeout=60000)
public void testQuotaStateUpdateTableThrottle() {
final TableName tableNameA = TableName.valueOf(name.getMethodName() + "A");
final TableName tableNameB = TableName.valueOf(name.getMethodName() + "B");
final TableName tableNameC = TableName.valueOf(name.getMethodName() + "C");
final int TABLE_A_THROTTLE_1 = 3;
final int TABLE_A_THROTTLE_2 = 11;
final int TABLE_B_THROTTLE = 4;
final int TABLE_C_THROTTLE = 5;
final long LAST_UPDATE_1 = 10;
final long LAST_UPDATE_2 = 20;
final long LAST_UPDATE_3 = 30;
UserQuotaState quotaInfo = new UserQuotaState();
assertEquals(0, quotaInfo.getLastUpdate());
assertTrue(quotaInfo.isBypass());
// Add A B table limiters
UserQuotaState otherQuotaState = new UserQuotaState(LAST_UPDATE_1);
otherQuotaState.setQuotas(tableNameA, buildReqNumThrottle(TABLE_A_THROTTLE_1));
otherQuotaState.setQuotas(tableNameB, buildReqNumThrottle(TABLE_B_THROTTLE));
assertEquals(LAST_UPDATE_1, otherQuotaState.getLastUpdate());
assertFalse(otherQuotaState.isBypass());
quotaInfo.update(otherQuotaState);
assertEquals(LAST_UPDATE_1, quotaInfo.getLastUpdate());
assertFalse(quotaInfo.isBypass());
assertThrottleException(quotaInfo.getTableLimiter(tableNameA), TABLE_A_THROTTLE_1);
assertThrottleException(quotaInfo.getTableLimiter(tableNameB), TABLE_B_THROTTLE);
assertNoopLimiter(quotaInfo.getTableLimiter(tableNameC));
// Add C, Remove B, Update A table limiters
otherQuotaState = new UserQuotaState(LAST_UPDATE_2);
otherQuotaState.setQuotas(tableNameA, buildReqNumThrottle(TABLE_A_THROTTLE_2));
otherQuotaState.setQuotas(tableNameC, buildReqNumThrottle(TABLE_C_THROTTLE));
assertEquals(LAST_UPDATE_2, otherQuotaState.getLastUpdate());
assertFalse(otherQuotaState.isBypass());
quotaInfo.update(otherQuotaState);
assertEquals(LAST_UPDATE_2, quotaInfo.getLastUpdate());
assertFalse(quotaInfo.isBypass());
assertThrottleException(quotaInfo.getTableLimiter(tableNameA),
TABLE_A_THROTTLE_2 - TABLE_A_THROTTLE_1);
assertThrottleException(quotaInfo.getTableLimiter(tableNameC), TABLE_C_THROTTLE);
assertNoopLimiter(quotaInfo.getTableLimiter(tableNameB));
// Remove table limiters
otherQuotaState = new UserQuotaState(LAST_UPDATE_3);
assertEquals(LAST_UPDATE_3, otherQuotaState.getLastUpdate());
assertTrue(otherQuotaState.isBypass());
quotaInfo.update(otherQuotaState);
assertEquals(LAST_UPDATE_3, quotaInfo.getLastUpdate());
assertTrue(quotaInfo.isBypass());
assertNoopLimiter(quotaInfo.getTableLimiter(UNKNOWN_TABLE_NAME));
}
private Quotas buildReqNumThrottle(final long limit) {
return Quotas.newBuilder()
.setThrottle(Throttle.newBuilder()
.setReqNum(ProtobufUtil.toTimedQuota(limit, TimeUnit.MINUTES, QuotaScope.MACHINE))
.build())
.build();
}
private void assertThrottleException(final QuotaLimiter limiter, final int availReqs) {
assertNoThrottleException(limiter, availReqs);
try {
limiter.checkQuota(1, 1);
fail("Should have thrown ThrottlingException");
} catch (ThrottlingException e) {
// expected
}
}
private void assertNoThrottleException(final QuotaLimiter limiter, final int availReqs) {
for (int i = 0; i < availReqs; ++i) {
try {
limiter.checkQuota(1, 1);
} catch (ThrottlingException e) {
fail("Unexpected ThrottlingException after " + i + " requests. limit=" + availReqs);
}
limiter.grabQuota(1, 1);
}
}
private void assertNoopLimiter(final QuotaLimiter limiter) {
assertTrue(limiter == NoopQuotaLimiter.get());
assertNoThrottleException(limiter, 100);
}
}