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