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 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.quotas;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertFalse;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023
024import java.util.HashMap;
025import java.util.Map;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.HBaseConfiguration;
028import org.apache.hadoop.hbase.testclassification.RegionServerTests;
029import org.apache.hadoop.hbase.testclassification.SmallTests;
030import org.junit.jupiter.api.Tag;
031import org.junit.jupiter.api.Test;
032
033import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
034import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos;
035
036/**
037 * Tests of QuotaCache that don't require a minicluster, unlike in TestQuotaCache
038 */
039@Tag(RegionServerTests.TAG)
040@Tag(SmallTests.TAG)
041public class TestQuotaCache2 {
042
043  private static final Configuration conf = HBaseConfiguration.create();
044
045  @Test
046  public void testPreserveLimiterAvailability() throws Exception {
047    // establish old cache with a limiter for 100 read bytes per second
048    QuotaState oldState = new QuotaState();
049    Map<String, QuotaState> oldCache = new HashMap<>();
050    oldCache.put("my_table", oldState);
051    QuotaProtos.Throttle throttle1 = QuotaProtos.Throttle.newBuilder()
052      .setReadSize(QuotaProtos.TimedQuota.newBuilder().setTimeUnit(HBaseProtos.TimeUnit.SECONDS)
053        .setSoftLimit(100).setScope(QuotaProtos.QuotaScope.MACHINE).build())
054      .build();
055    QuotaLimiter limiter1 = TimeBasedLimiter.fromThrottle(conf, throttle1);
056    oldState.setGlobalLimiter(limiter1);
057
058    // consume one byte from the limiter, so 99 will be left
059    limiter1.consumeRead(1, 1, false);
060
061    // establish new cache, also with a limiter for 100 read bytes per second
062    QuotaState newState = new QuotaState();
063    Map<String, QuotaState> newCache = new HashMap<>();
064    newCache.put("my_table", newState);
065    QuotaProtos.Throttle throttle2 = QuotaProtos.Throttle.newBuilder()
066      .setReadSize(QuotaProtos.TimedQuota.newBuilder().setTimeUnit(HBaseProtos.TimeUnit.SECONDS)
067        .setSoftLimit(100).setScope(QuotaProtos.QuotaScope.MACHINE).build())
068      .build();
069    QuotaLimiter limiter2 = TimeBasedLimiter.fromThrottle(conf, throttle2);
070    newState.setGlobalLimiter(limiter2);
071
072    // update new cache from old cache
073    QuotaCache.updateNewCacheFromOld(oldCache, newCache);
074
075    // verify that the 99 available bytes from the limiter was carried over
076    TimeBasedLimiter updatedLimiter =
077      (TimeBasedLimiter) newCache.get("my_table").getGlobalLimiter();
078    assertEquals(99, updatedLimiter.getReadAvailable());
079  }
080
081  @Test
082  public void testClobberLimiterLimit() throws Exception {
083    // establish old cache with a limiter for 100 read bytes per second
084    QuotaState oldState = new QuotaState();
085    Map<String, QuotaState> oldCache = new HashMap<>();
086    oldCache.put("my_table", oldState);
087    QuotaProtos.Throttle throttle1 = QuotaProtos.Throttle.newBuilder()
088      .setReadSize(QuotaProtos.TimedQuota.newBuilder().setTimeUnit(HBaseProtos.TimeUnit.SECONDS)
089        .setSoftLimit(100).setScope(QuotaProtos.QuotaScope.MACHINE).build())
090      .build();
091    QuotaLimiter limiter1 = TimeBasedLimiter.fromThrottle(conf, throttle1);
092    oldState.setGlobalLimiter(limiter1);
093
094    // establish new cache, also with a limiter for 100 read bytes per second
095    QuotaState newState = new QuotaState();
096    Map<String, QuotaState> newCache = new HashMap<>();
097    newCache.put("my_table", newState);
098    QuotaProtos.Throttle throttle2 = QuotaProtos.Throttle.newBuilder()
099      .setReadSize(QuotaProtos.TimedQuota.newBuilder().setTimeUnit(HBaseProtos.TimeUnit.SECONDS)
100        .setSoftLimit(50).setScope(QuotaProtos.QuotaScope.MACHINE).build())
101      .build();
102    QuotaLimiter limiter2 = TimeBasedLimiter.fromThrottle(conf, throttle2);
103    newState.setGlobalLimiter(limiter2);
104
105    // update new cache from old cache
106    QuotaCache.updateNewCacheFromOld(oldCache, newCache);
107
108    // verify that the 99 available bytes from the limiter was carried over
109    TimeBasedLimiter updatedLimiter =
110      (TimeBasedLimiter) newCache.get("my_table").getGlobalLimiter();
111    assertEquals(50, updatedLimiter.getReadLimit());
112  }
113
114  @Test
115  public void testForgetsDeletedQuota() {
116    QuotaState oldState = new QuotaState();
117    Map<String, QuotaState> oldCache = new HashMap<>();
118    oldCache.put("my_table1", oldState);
119
120    QuotaState newState = new QuotaState();
121    Map<String, QuotaState> newCache = new HashMap<>();
122    newCache.put("my_table2", newState);
123
124    QuotaCache.updateNewCacheFromOld(oldCache, newCache);
125
126    assertTrue(newCache.containsKey("my_table2"));
127    assertFalse(newCache.containsKey("my_table1"));
128  }
129
130  @Test
131  public void testLearnsNewQuota() {
132    Map<String, QuotaState> oldCache = new HashMap<>();
133
134    QuotaState newState = new QuotaState();
135    Map<String, QuotaState> newCache = new HashMap<>();
136    newCache.put("my_table1", newState);
137
138    QuotaCache.updateNewCacheFromOld(oldCache, newCache);
139
140    assertTrue(newCache.containsKey("my_table1"));
141  }
142
143  @Test
144  public void testUserSpecificOverridesDefaultNewQuota() {
145    // establish old cache with a limiter for 100 read bytes per second
146    QuotaState oldState = new QuotaState();
147    Map<String, QuotaState> oldCache = new HashMap<>();
148    oldCache.put("my_table", oldState);
149    QuotaProtos.Throttle throttle1 = QuotaProtos.Throttle.newBuilder()
150      .setReadSize(QuotaProtos.TimedQuota.newBuilder().setTimeUnit(HBaseProtos.TimeUnit.SECONDS)
151        .setSoftLimit(100).setScope(QuotaProtos.QuotaScope.MACHINE).build())
152      .build();
153    QuotaLimiter limiter1 = TimeBasedLimiter.fromThrottle(conf, throttle1);
154    oldState.setGlobalLimiter(limiter1);
155
156    // establish new cache, with a limiter for 999 read bytes per second
157    QuotaState newState = new QuotaState();
158    Map<String, QuotaState> newCache = new HashMap<>();
159    newCache.put("my_table", newState);
160    QuotaProtos.Throttle throttle2 = QuotaProtos.Throttle.newBuilder()
161      .setReadSize(QuotaProtos.TimedQuota.newBuilder().setTimeUnit(HBaseProtos.TimeUnit.SECONDS)
162        .setSoftLimit(999).setScope(QuotaProtos.QuotaScope.MACHINE).build())
163      .build();
164    QuotaLimiter limiter2 = TimeBasedLimiter.fromThrottle(conf, throttle2);
165    newState.setGlobalLimiter(limiter2);
166
167    // update new cache from old cache
168    QuotaCache.updateNewCacheFromOld(oldCache, newCache);
169
170    // verify that the 999 available bytes from the limiter was carried over
171    TimeBasedLimiter updatedLimiter =
172      (TimeBasedLimiter) newCache.get("my_table").getGlobalLimiter();
173    assertEquals(999, updatedLimiter.getReadAvailable());
174  }
175}