View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional information regarding
4    * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
7    * law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
8    * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
9    * for the specific language governing permissions and limitations under the License.
10   */
11  
12  package org.apache.hadoop.hbase.quotas;
13  
14  import static org.junit.Assert.assertEquals;
15  import static org.junit.Assert.assertFalse;
16  import static org.junit.Assert.assertTrue;
17  
18  import java.util.concurrent.TimeUnit;
19  
20  import org.apache.hadoop.hbase.testclassification.SmallTests;
21  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
22  import org.junit.Test;
23  import org.junit.experimental.categories.Category;
24  
25  /**
26   * Verify the behaviour of the Rate Limiter.
27   */
28  @Category({ SmallTests.class })
29  public class TestRateLimiter {
30    @Test
31    public void testWaitIntervalTimeUnitSeconds() {
32      testWaitInterval(TimeUnit.SECONDS, 10, 100);
33    }
34  
35    @Test
36    public void testWaitIntervalTimeUnitMinutes() {
37      testWaitInterval(TimeUnit.MINUTES, 10, 6000);
38    }
39  
40    @Test
41    public void testWaitIntervalTimeUnitHours() {
42      testWaitInterval(TimeUnit.HOURS, 10, 360000);
43    }
44  
45    @Test
46    public void testWaitIntervalTimeUnitDays() {
47      testWaitInterval(TimeUnit.DAYS, 10, 8640000);
48    }
49  
50    private void testWaitInterval(final TimeUnit timeUnit, final long limit,
51        final long expectedWaitInterval) {
52      RateLimiter limiter = new AverageIntervalRateLimiter();
53      limiter.set(limit, timeUnit);
54  
55      long nowTs = 0;
56      // consume all the available resources, one request at the time.
57      // the wait interval should be 0
58      for (int i = 0; i < (limit - 1); ++i) {
59        assertTrue(limiter.canExecute());
60        limiter.consume();
61        long waitInterval = limiter.waitInterval();
62        assertEquals(0, waitInterval);
63      }
64  
65      for (int i = 0; i < (limit * 4); ++i) {
66        // There is one resource available, so we should be able to
67        // consume it without waiting.
68        limiter.setNextRefillTime(limiter.getNextRefillTime() - nowTs);
69        assertTrue(limiter.canExecute());
70        assertEquals(0, limiter.waitInterval());
71        limiter.consume();
72        // No more resources are available, we should wait for at least an interval.
73        long waitInterval = limiter.waitInterval();
74        assertEquals(expectedWaitInterval, waitInterval);
75  
76        // set the nowTs to be the exact time when resources should be available again.
77        nowTs = waitInterval;
78  
79        // artificially go into the past to prove that when too early we should fail.
80        long temp = nowTs + 500;
81        limiter.setNextRefillTime(limiter.getNextRefillTime() + temp);
82        assertFalse(limiter.canExecute());
83        //Roll back the nextRefillTime set to continue further testing
84        limiter.setNextRefillTime(limiter.getNextRefillTime() - temp);
85      }
86    }
87  
88    @Test
89    public void testOverconsumptionAverageIntervalRefillStrategy() {
90      RateLimiter limiter = new AverageIntervalRateLimiter();
91      limiter.set(10, TimeUnit.SECONDS);
92  
93      // 10 resources are available, but we need to consume 20 resources
94      // Verify that we have to wait at least 1.1sec to have 1 resource available
95      assertTrue(limiter.canExecute());
96      limiter.consume(20);
97      // To consume 1 resource wait for 100ms
98      assertEquals(100, limiter.waitInterval(1));
99      // To consume 10 resource wait for 1000ms
100     assertEquals(1000, limiter.waitInterval(10));
101 
102     limiter.setNextRefillTime(limiter.getNextRefillTime() - 900);
103     // Verify that after 1sec the 1 resource is available
104     assertTrue(limiter.canExecute(1));
105     limiter.setNextRefillTime(limiter.getNextRefillTime() - 100);
106     // Verify that after 1sec the 10 resource is available
107     assertTrue(limiter.canExecute());
108     assertEquals(0, limiter.waitInterval());
109   }
110 
111   @Test
112   public void testOverconsumptionFixedIntervalRefillStrategy() throws InterruptedException {
113     RateLimiter limiter = new FixedIntervalRateLimiter();
114     limiter.set(10, TimeUnit.SECONDS);
115 
116     // 10 resources are available, but we need to consume 20 resources
117     // Verify that we have to wait at least 1.1sec to have 1 resource available
118     assertTrue(limiter.canExecute());
119     limiter.consume(20);
120     // To consume 1 resource also wait for 1000ms
121     assertEquals(1000, limiter.waitInterval(1));
122     // To consume 10 resource wait for 100ms
123     assertEquals(1000, limiter.waitInterval(10));
124 
125     limiter.setNextRefillTime(limiter.getNextRefillTime() - 900);
126     // Verify that after 1sec also no resource should be available
127     assertFalse(limiter.canExecute(1));
128     limiter.setNextRefillTime(limiter.getNextRefillTime() - 100);
129 
130     // Verify that after 1sec the 10 resource is available
131     assertTrue(limiter.canExecute());
132     assertEquals(0, limiter.waitInterval());
133   }
134 
135   @Test
136   public void testFixedIntervalResourceAvailability() throws Exception {
137     RateLimiter limiter = new FixedIntervalRateLimiter();
138     limiter.set(10, TimeUnit.SECONDS);
139 
140     assertTrue(limiter.canExecute(10));
141     limiter.consume(3);
142     assertEquals(7, limiter.getAvailable());
143     assertFalse(limiter.canExecute(10));
144     limiter.setNextRefillTime(limiter.getNextRefillTime() - 1000);
145     assertTrue(limiter.canExecute(10));
146     assertEquals(10, limiter.getAvailable());
147   }
148 
149   @Test
150   public void testLimiterBySmallerRate() throws InterruptedException {
151     // set limiter is 10 resources per seconds
152     RateLimiter limiter = new FixedIntervalRateLimiter();
153     limiter.set(10, TimeUnit.SECONDS);
154 
155     int count = 0; // control the test count
156     while ((count++) < 10) {
157       // test will get 3 resources per 0.5 sec. so it will get 6 resources per sec.
158       limiter.setNextRefillTime(limiter.getNextRefillTime() - 500);
159       for (int i = 0; i < 3; i++) {
160         // 6 resources/sec < limit, so limiter.canExecute(nowTs, lastTs) should be true
161         assertEquals(true, limiter.canExecute());
162         limiter.consume();
163       }
164     }
165   }
166 
167   @Test
168   public void testCanExecuteOfAverageIntervalRateLimiter() throws InterruptedException {
169     RateLimiter limiter = new AverageIntervalRateLimiter();
170     // when set limit is 100 per sec, this AverageIntervalRateLimiter will support at max 200 per sec
171     limiter.set(100, TimeUnit.SECONDS);
172     limiter.setNextRefillTime(EnvironmentEdgeManager.currentTime());
173     assertEquals(50, testCanExecuteByRate(limiter, 50));
174 
175     // refill the avail to limit
176     limiter.set(100, TimeUnit.SECONDS);
177     limiter.setNextRefillTime(EnvironmentEdgeManager.currentTime());
178     assertEquals(100, testCanExecuteByRate(limiter, 100));
179 
180     // refill the avail to limit
181     limiter.set(100, TimeUnit.SECONDS);
182     limiter.setNextRefillTime(EnvironmentEdgeManager.currentTime());
183     assertEquals(200, testCanExecuteByRate(limiter, 200));
184 
185     // refill the avail to limit
186     limiter.set(100, TimeUnit.SECONDS);
187     limiter.setNextRefillTime(EnvironmentEdgeManager.currentTime());
188     assertEquals(200, testCanExecuteByRate(limiter, 500));
189   }
190 
191   @Test
192   public void testCanExecuteOfFixedIntervalRateLimiter() throws InterruptedException {
193     RateLimiter limiter = new FixedIntervalRateLimiter();
194     // when set limit is 100 per sec, this FixedIntervalRateLimiter will support at max 100 per sec
195     limiter.set(100, TimeUnit.SECONDS);
196     limiter.setNextRefillTime(EnvironmentEdgeManager.currentTime());
197     assertEquals(50, testCanExecuteByRate(limiter, 50));
198 
199     // refill the avail to limit
200     limiter.set(100, TimeUnit.SECONDS);
201     limiter.setNextRefillTime(EnvironmentEdgeManager.currentTime());
202     assertEquals(100, testCanExecuteByRate(limiter, 100));
203 
204     // refill the avail to limit
205     limiter.set(100, TimeUnit.SECONDS);
206     limiter.setNextRefillTime(EnvironmentEdgeManager.currentTime());
207     assertEquals(100, testCanExecuteByRate(limiter, 200));
208   }
209 
210   public int testCanExecuteByRate(RateLimiter limiter, int rate) {
211     int request = 0;
212     int count = 0;
213     while ((request++) < rate) {
214       limiter.setNextRefillTime(limiter.getNextRefillTime() - limiter.getTimeUnitInMillis() / rate);
215       if (limiter.canExecute()) {
216         count++;
217         limiter.consume();
218       }
219     }
220     return count;
221   }
222 
223   @Test
224   public void testRefillOfAverageIntervalRateLimiter() throws InterruptedException {
225     RateLimiter limiter = new AverageIntervalRateLimiter();
226     limiter.set(60, TimeUnit.SECONDS);
227     assertEquals(60, limiter.getAvailable());
228     // first refill, will return the number same with limit
229     assertEquals(60, limiter.refill(limiter.getLimit()));
230 
231     limiter.consume(30);
232 
233     // after 0.2 sec, refill should return 12
234     limiter.setNextRefillTime(limiter.getNextRefillTime() - 200);
235     assertEquals(12, limiter.refill(limiter.getLimit()));
236 
237     // after 0.5 sec, refill should return 30
238     limiter.setNextRefillTime(limiter.getNextRefillTime() - 500);
239     assertEquals(30, limiter.refill(limiter.getLimit()));
240 
241     // after 1 sec, refill should return 60
242     limiter.setNextRefillTime(limiter.getNextRefillTime() - 1000);
243     assertEquals(60, limiter.refill(limiter.getLimit()));
244 
245     // after more than 1 sec, refill should return at max 60
246     limiter.setNextRefillTime(limiter.getNextRefillTime() - 3000);
247     assertEquals(60, limiter.refill(limiter.getLimit()));
248     limiter.setNextRefillTime(limiter.getNextRefillTime() - 5000);
249     assertEquals(60, limiter.refill(limiter.getLimit()));
250   }
251 
252   @Test
253   public void testRefillOfFixedIntervalRateLimiter() throws InterruptedException {
254     RateLimiter limiter = new FixedIntervalRateLimiter();
255     limiter.set(60, TimeUnit.SECONDS);
256     assertEquals(60, limiter.getAvailable());
257     // first refill, will return the number same with limit
258     assertEquals(60, limiter.refill(limiter.getLimit()));
259 
260     limiter.consume(30);
261 
262     // after 0.2 sec, refill should return 0
263     limiter.setNextRefillTime(limiter.getNextRefillTime() - 200);
264     assertEquals(0, limiter.refill(limiter.getLimit()));
265 
266     // after 0.5 sec, refill should return 0
267     limiter.setNextRefillTime(limiter.getNextRefillTime() - 500);
268     assertEquals(0, limiter.refill(limiter.getLimit()));
269 
270     // after 1 sec, refill should return 60
271     limiter.setNextRefillTime(limiter.getNextRefillTime() - 1000);
272     assertEquals(60, limiter.refill(limiter.getLimit()));
273 
274     // after more than 1 sec, refill should return at max 60
275     limiter.setNextRefillTime(limiter.getNextRefillTime() - 3000);
276     assertEquals(60, limiter.refill(limiter.getLimit()));
277     limiter.setNextRefillTime(limiter.getNextRefillTime() - 5000);
278     assertEquals(60, limiter.refill(limiter.getLimit()));
279   }
280 }