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