1 /* 2 * Copyright (C) 2012 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.util.concurrent; 18 19 import static java.lang.reflect.Modifier.isStatic; 20 import static java.util.concurrent.TimeUnit.MICROSECONDS; 21 import static java.util.concurrent.TimeUnit.MILLISECONDS; 22 import static java.util.concurrent.TimeUnit.NANOSECONDS; 23 import static java.util.concurrent.TimeUnit.SECONDS; 24 25 import com.google.common.collect.ImmutableClassToInstanceMap; 26 import com.google.common.collect.ImmutableSet; 27 import com.google.common.collect.Lists; 28 import com.google.common.testing.NullPointerTester; 29 import com.google.common.testing.NullPointerTester.Visibility; 30 import com.google.common.util.concurrent.RateLimiter.SleepingStopwatch; 31 32 import junit.framework.TestCase; 33 34 import org.easymock.EasyMock; 35 import org.mockito.Mockito; 36 37 import java.lang.reflect.Method; 38 import java.util.Arrays; 39 import java.util.List; 40 import java.util.Random; 41 import java.util.concurrent.TimeUnit; 42 43 /** 44 * Tests for RateLimiter. 45 * 46 * @author Dimitris Andreou 47 */ 48 public class RateLimiterTest extends TestCase { 49 private static final double EPSILON = 1e-8; 50 51 private final FakeStopwatch stopwatch = new FakeStopwatch(); 52 testSimple()53 public void testSimple() { 54 RateLimiter limiter = RateLimiter.create(stopwatch, 5.0); 55 limiter.acquire(); // R0.00, since it's the first request 56 limiter.acquire(); // R0.20 57 limiter.acquire(); // R0.20 58 assertEvents("R0.00", "R0.20", "R0.20"); 59 } 60 testImmediateTryAcquire()61 public void testImmediateTryAcquire() { 62 RateLimiter r = RateLimiter.create(1); 63 assertTrue("Unable to acquire initial permit", r.tryAcquire()); 64 assertFalse("Capable of acquiring secondary permit", r.tryAcquire()); 65 } 66 testSimpleRateUpdate()67 public void testSimpleRateUpdate() { 68 RateLimiter limiter = RateLimiter.create(5.0, 5, SECONDS); 69 assertEquals(5.0, limiter.getRate()); 70 limiter.setRate(10.0); 71 assertEquals(10.0, limiter.getRate()); 72 73 try { 74 limiter.setRate(0.0); 75 fail(); 76 } catch (IllegalArgumentException expected) {} 77 try { 78 limiter.setRate(-10.0); 79 fail(); 80 } catch (IllegalArgumentException expected) {} 81 } 82 testAcquireParameterValidation()83 public void testAcquireParameterValidation() { 84 RateLimiter limiter = RateLimiter.create(999); 85 try { 86 limiter.acquire(0); 87 fail(); 88 } catch (IllegalArgumentException expected) { 89 } 90 try { 91 limiter.acquire(-1); 92 fail(); 93 } catch (IllegalArgumentException expected) { 94 } 95 try { 96 limiter.tryAcquire(0); 97 fail(); 98 } catch (IllegalArgumentException expected) { 99 } 100 try { 101 limiter.tryAcquire(-1); 102 fail(); 103 } catch (IllegalArgumentException expected) { 104 } 105 try { 106 limiter.tryAcquire(0, 1, SECONDS); 107 fail(); 108 } catch (IllegalArgumentException expected) { 109 } 110 try { 111 limiter.tryAcquire(-1, 1, SECONDS); 112 fail(); 113 } catch (IllegalArgumentException expected) { 114 } 115 } 116 testSimpleWithWait()117 public void testSimpleWithWait() { 118 RateLimiter limiter = RateLimiter.create(stopwatch, 5.0); 119 limiter.acquire(); // R0.00 120 stopwatch.sleepMillis(200); // U0.20, we are ready for the next request... 121 limiter.acquire(); // R0.00, ...which is granted immediately 122 limiter.acquire(); // R0.20 123 assertEvents("R0.00", "U0.20", "R0.00", "R0.20"); 124 } 125 testSimpleAcquireReturnValues()126 public void testSimpleAcquireReturnValues() { 127 RateLimiter limiter = RateLimiter.create(stopwatch, 5.0); 128 assertEquals(0.0, limiter.acquire(), EPSILON); // R0.00 129 stopwatch.sleepMillis(200); // U0.20, we are ready for the next request... 130 assertEquals(0.0, limiter.acquire(), EPSILON); // R0.00, ...which is granted immediately 131 assertEquals(0.2, limiter.acquire(), EPSILON); // R0.20 132 assertEvents("R0.00", "U0.20", "R0.00", "R0.20"); 133 } 134 testSimpleAcquireEarliestAvailableIsInPast()135 public void testSimpleAcquireEarliestAvailableIsInPast() { 136 RateLimiter limiter = RateLimiter.create(stopwatch, 5.0); 137 assertEquals(0.0, limiter.acquire(), EPSILON); 138 stopwatch.sleepMillis(400); 139 assertEquals(0.0, limiter.acquire(), EPSILON); 140 assertEquals(0.0, limiter.acquire(), EPSILON); 141 assertEquals(0.2, limiter.acquire(), EPSILON); 142 } 143 testOneSecondBurst()144 public void testOneSecondBurst() { 145 RateLimiter limiter = RateLimiter.create(stopwatch, 5.0); 146 stopwatch.sleepMillis(1000); // max capacity reached 147 stopwatch.sleepMillis(1000); // this makes no difference 148 limiter.acquire(1); // R0.00, since it's the first request 149 150 limiter.acquire(1); // R0.00, from capacity 151 limiter.acquire(3); // R0.00, from capacity 152 limiter.acquire(1); // R0.00, concluding a burst of 5 permits 153 154 limiter.acquire(); // R0.20, capacity exhausted 155 assertEvents("U1.00", "U1.00", 156 "R0.00", "R0.00", "R0.00", "R0.00", // first request and burst 157 "R0.20"); 158 } 159 testCreateWarmupParameterValidation()160 public void testCreateWarmupParameterValidation() { 161 RateLimiter.create(1.0, 1, NANOSECONDS); 162 RateLimiter.create(1.0, 0, NANOSECONDS); 163 164 try { 165 RateLimiter.create(0.0, 1, NANOSECONDS); 166 fail(); 167 } catch (IllegalArgumentException expected) { 168 } 169 170 try { 171 RateLimiter.create(1.0, -1, NANOSECONDS); 172 fail(); 173 } catch (IllegalArgumentException expected) { 174 } 175 } 176 testWarmUp()177 public void testWarmUp() { 178 RateLimiter limiter = RateLimiter.create(stopwatch, 2.0, 4000, MILLISECONDS); 179 for (int i = 0; i < 8; i++) { 180 limiter.acquire(); // #1 181 } 182 stopwatch.sleepMillis(500); // #2: to repay for the last acquire 183 stopwatch.sleepMillis(4000); // #3: becomes cold again 184 for (int i = 0; i < 8; i++) { 185 limiter.acquire(); // // #4 186 } 187 stopwatch.sleepMillis(500); // #5: to repay for the last acquire 188 stopwatch.sleepMillis(2000); // #6: didn't get cold! It would take another 2 seconds to go cold 189 for (int i = 0; i < 8; i++) { 190 limiter.acquire(); // #7 191 } 192 assertEvents( 193 "R0.00, R1.38, R1.13, R0.88, R0.63, R0.50, R0.50, R0.50", // #1 194 "U0.50", // #2 195 "U4.00", // #3 196 "R0.00, R1.38, R1.13, R0.88, R0.63, R0.50, R0.50, R0.50", // #4 197 "U0.50", // #5 198 "U2.00", // #6 199 "R0.00, R0.50, R0.50, R0.50, R0.50, R0.50, R0.50, R0.50"); // #7 200 } 201 testWarmUpAndUpdate()202 public void testWarmUpAndUpdate() { 203 RateLimiter limiter = RateLimiter.create(stopwatch, 2.0, 4000, MILLISECONDS); 204 for (int i = 0; i < 8; i++) { 205 limiter.acquire(); // // #1 206 } 207 stopwatch.sleepMillis(4500); // #2: back to cold state (warmup period + repay last acquire) 208 for (int i = 0; i < 3; i++) { // only three steps, we're somewhere in the warmup period 209 limiter.acquire(); // #3 210 } 211 212 limiter.setRate(4.0); // double the rate! 213 limiter.acquire(); // #4, we repay the debt of the last acquire (imposed by the old rate) 214 for (int i = 0; i < 4; i++) { 215 limiter.acquire(); // #5 216 } 217 stopwatch.sleepMillis(4250); // #6, back to cold state (warmup period + repay last acquire) 218 for (int i = 0; i < 11; i++) { 219 limiter.acquire(); // #7, showing off the warmup starting from totally cold 220 } 221 222 // make sure the areas (times) remain the same, while permits are different 223 assertEvents( 224 "R0.00, R1.38, R1.13, R0.88, R0.63, R0.50, R0.50, R0.50", // #1 225 "U4.50", // #2 226 "R0.00, R1.38, R1.13", // #3, after that the rate changes 227 "R0.88", // #4, this is what the throttling would be with the old rate 228 "R0.34, R0.28, R0.25, R0.25", // #5 229 "U4.25", // #6 230 "R0.00, R0.72, R0.66, R0.59, R0.53, R0.47, R0.41", // #7 231 "R0.34, R0.28, R0.25, R0.25"); // #7 (cont.), note, this matches #5 232 } 233 testBurstyAndUpdate()234 public void testBurstyAndUpdate() { 235 RateLimiter rateLimiter = RateLimiter.create(stopwatch, 1.0); 236 rateLimiter.acquire(1); // no wait 237 rateLimiter.acquire(1); // R1.00, to repay previous 238 239 rateLimiter.setRate(2.0); // update the rate! 240 241 rateLimiter.acquire(1); // R1.00, to repay previous (the previous was under the old rate!) 242 rateLimiter.acquire(2); // R0.50, to repay previous (now the rate takes effect) 243 rateLimiter.acquire(4); // R1.00, to repay previous 244 rateLimiter.acquire(1); // R2.00, to repay previous 245 assertEvents("R0.00", "R1.00", "R1.00", "R0.50", "R1.00", "R2.00"); 246 } 247 testTryAcquire_noWaitAllowed()248 public void testTryAcquire_noWaitAllowed() { 249 RateLimiter limiter = RateLimiter.create(stopwatch, 5.0); 250 assertTrue(limiter.tryAcquire(0, SECONDS)); 251 assertFalse(limiter.tryAcquire(0, SECONDS)); 252 assertFalse(limiter.tryAcquire(0, SECONDS)); 253 stopwatch.sleepMillis(100); 254 assertFalse(limiter.tryAcquire(0, SECONDS)); 255 } 256 testTryAcquire_someWaitAllowed()257 public void testTryAcquire_someWaitAllowed() { 258 RateLimiter limiter = RateLimiter.create(stopwatch, 5.0); 259 assertTrue(limiter.tryAcquire(0, SECONDS)); 260 assertTrue(limiter.tryAcquire(200, MILLISECONDS)); 261 assertFalse(limiter.tryAcquire(100, MILLISECONDS)); 262 stopwatch.sleepMillis(100); 263 assertTrue(limiter.tryAcquire(100, MILLISECONDS)); 264 } 265 testTryAcquire_overflow()266 public void testTryAcquire_overflow() { 267 RateLimiter limiter = RateLimiter.create(stopwatch, 5.0); 268 assertTrue(limiter.tryAcquire(0, MICROSECONDS)); 269 stopwatch.sleepMillis(100); 270 assertTrue(limiter.tryAcquire(Long.MAX_VALUE, MICROSECONDS)); 271 } 272 testTryAcquire_negative()273 public void testTryAcquire_negative() { 274 RateLimiter limiter = RateLimiter.create(stopwatch, 5.0); 275 assertTrue(limiter.tryAcquire(5, 0, SECONDS)); 276 stopwatch.sleepMillis(900); 277 assertFalse(limiter.tryAcquire(1, Long.MIN_VALUE, SECONDS)); 278 stopwatch.sleepMillis(100); 279 assertTrue(limiter.tryAcquire(1, -1, SECONDS)); 280 } 281 testSimpleWeights()282 public void testSimpleWeights() { 283 RateLimiter rateLimiter = RateLimiter.create(stopwatch, 1.0); 284 rateLimiter.acquire(1); // no wait 285 rateLimiter.acquire(1); // R1.00, to repay previous 286 rateLimiter.acquire(2); // R1.00, to repay previous 287 rateLimiter.acquire(4); // R2.00, to repay previous 288 rateLimiter.acquire(8); // R4.00, to repay previous 289 rateLimiter.acquire(1); // R8.00, to repay previous 290 assertEvents("R0.00", "R1.00", "R1.00", "R2.00", "R4.00", "R8.00"); 291 } 292 testInfinity_Bursty()293 public void testInfinity_Bursty() { 294 RateLimiter limiter = RateLimiter.create(stopwatch, Double.POSITIVE_INFINITY); 295 limiter.acquire(Integer.MAX_VALUE / 4); 296 limiter.acquire(Integer.MAX_VALUE / 2); 297 limiter.acquire(Integer.MAX_VALUE); 298 assertEvents("R0.00", "R0.00", "R0.00"); // no wait, infinite rate! 299 300 limiter.setRate(2.0); 301 limiter.acquire(); 302 limiter.acquire(); 303 limiter.acquire(); 304 limiter.acquire(); 305 limiter.acquire(); 306 assertEvents( 307 "R0.00", // First comes the saved-up burst, which defaults to a 1-second burst (2 requests). 308 "R0.00", 309 "R0.00", // Now comes the free request. 310 "R0.50", // Now it's 0.5 seconds per request. 311 "R0.50"); 312 313 limiter.setRate(Double.POSITIVE_INFINITY); 314 limiter.acquire(); 315 limiter.acquire(); 316 limiter.acquire(); 317 assertEvents("R0.50", "R0.00", "R0.00"); // we repay the last request (.5sec), then back to +oo 318 } 319 320 /** https://code.google.com/p/guava-libraries/issues/detail?id=1791 */ testInfinity_BustyTimeElapsed()321 public void testInfinity_BustyTimeElapsed() { 322 RateLimiter limiter = RateLimiter.create(stopwatch, Double.POSITIVE_INFINITY); 323 stopwatch.instant += 1000000; 324 limiter.setRate(2.0); 325 for (int i = 0; i < 5; i++) { 326 limiter.acquire(); 327 } 328 assertEvents( 329 "R0.00", // First comes the saved-up burst, which defaults to a 1-second burst (2 requests). 330 "R0.00", 331 "R0.00", // Now comes the free request. 332 "R0.50", // Now it's 0.5 seconds per request. 333 "R0.50"); 334 } 335 testInfinity_WarmUp()336 public void testInfinity_WarmUp() { 337 RateLimiter limiter = RateLimiter.create( 338 stopwatch, Double.POSITIVE_INFINITY, 10, SECONDS); 339 limiter.acquire(Integer.MAX_VALUE / 4); 340 limiter.acquire(Integer.MAX_VALUE / 2); 341 limiter.acquire(Integer.MAX_VALUE); 342 assertEvents("R0.00", "R0.00", "R0.00"); 343 344 limiter.setRate(1.0); 345 limiter.acquire(); 346 limiter.acquire(); 347 limiter.acquire(); 348 assertEvents("R0.00", "R1.00", "R1.00"); 349 350 limiter.setRate(Double.POSITIVE_INFINITY); 351 limiter.acquire(); 352 limiter.acquire(); 353 limiter.acquire(); 354 assertEvents("R1.00", "R0.00", "R0.00"); 355 } 356 testInfinity_WarmUpTimeElapsed()357 public void testInfinity_WarmUpTimeElapsed() { 358 RateLimiter limiter = RateLimiter.create(stopwatch, Double.POSITIVE_INFINITY, 10, SECONDS); 359 stopwatch.instant += 1000000; 360 limiter.setRate(1.0); 361 for (int i = 0; i < 5; i++) { 362 limiter.acquire(); 363 } 364 assertEvents("R0.00", "R1.00", "R1.00", "R1.00", "R1.00"); 365 } 366 367 /** 368 * Make sure that bursts can never go above 1-second-worth-of-work for the current 369 * rate, even when we change the rate. 370 */ testWeNeverGetABurstMoreThanOneSec()371 public void testWeNeverGetABurstMoreThanOneSec() { 372 RateLimiter limiter = RateLimiter.create(stopwatch, 1.0); 373 int[] rates = { 1000, 1, 10, 1000000, 10, 1}; 374 for (int rate : rates) { 375 int oneSecWorthOfWork = rate; 376 stopwatch.sleepMillis(rate * 1000); 377 limiter.setRate(rate); 378 long burst = measureTotalTimeMillis(limiter, oneSecWorthOfWork, new Random()); 379 // we allow one second worth of work to go in a burst (i.e. take less than a second) 380 assertTrue(burst <= 1000); 381 long afterBurst = measureTotalTimeMillis(limiter, oneSecWorthOfWork, new Random()); 382 // but work beyond that must take at least one second 383 assertTrue(afterBurst >= 1000); 384 } 385 } 386 387 /** 388 * This neat test shows that no matter what weights we use in our requests, if we push X 389 * amount of permits in a cool state, where X = rate * timeToCoolDown, and we have 390 * specified a timeToWarmUp() period, it will cost as the prescribed amount of time. E.g., 391 * calling [acquire(5), acquire(1)] takes exactly the same time as 392 * [acquire(2), acquire(3), acquire(1)]. 393 */ testTimeToWarmUpIsHonouredEvenWithWeights()394 public void testTimeToWarmUpIsHonouredEvenWithWeights() { 395 Random random = new Random(); 396 int maxPermits = 10; 397 double[] qpsToTest = { 4.0, 2.0, 1.0, 0.5, 0.1 }; 398 for (int trial = 0; trial < 100; trial++) { 399 for (double qps : qpsToTest) { 400 // Since we know that: maxPermits = 0.5 * warmup / stableInterval; 401 // then if maxPermits == 10, we have: 402 // warmupSeconds = 20 / qps 403 long warmupMillis = (long) ((2 * maxPermits / qps) * 1000.0); 404 RateLimiter rateLimiter = RateLimiter.create( 405 stopwatch, qps, warmupMillis, MILLISECONDS); 406 assertEquals(warmupMillis, measureTotalTimeMillis(rateLimiter, maxPermits, random)); 407 } 408 } 409 } 410 testNulls()411 public void testNulls() { 412 NullPointerTester tester = new NullPointerTester() 413 .setDefault(SleepingStopwatch.class, stopwatch) 414 .setDefault(int.class, 1); 415 tester.testStaticMethods(RateLimiter.class, Visibility.PACKAGE); 416 tester.testInstanceMethods(RateLimiter.create(stopwatch, 5.0), Visibility.PACKAGE); 417 } 418 measureTotalTimeMillis(RateLimiter rateLimiter, int permits, Random random)419 private long measureTotalTimeMillis(RateLimiter rateLimiter, int permits, Random random) { 420 long startTime = stopwatch.instant; 421 while (permits > 0) { 422 int nextPermitsToAcquire = Math.max(1, random.nextInt(permits)); 423 permits -= nextPermitsToAcquire; 424 rateLimiter.acquire(nextPermitsToAcquire); 425 } 426 rateLimiter.acquire(1); // to repay for any pending debt 427 return NANOSECONDS.toMillis(stopwatch.instant - startTime); 428 } 429 assertEvents(String... events)430 private void assertEvents(String... events) { 431 assertEquals(Arrays.toString(events), stopwatch.readEventsAndClear()); 432 } 433 434 /** 435 * The stopwatch gathers events and presents them as strings. 436 * R0.6 means a delay of 0.6 seconds caused by the (R)ateLimiter 437 * U1.0 means the (U)ser caused the stopwatch to sleep for a second. 438 */ 439 static class FakeStopwatch extends SleepingStopwatch { 440 long instant = 0L; 441 final List<String> events = Lists.newArrayList(); 442 443 @Override readMicros()444 public long readMicros() { 445 return NANOSECONDS.toMicros(instant); 446 } 447 sleepMillis(int millis)448 void sleepMillis(int millis) { 449 sleepMicros("U", MILLISECONDS.toMicros(millis)); 450 } 451 sleepMicros(String caption, long micros)452 void sleepMicros(String caption, long micros) { 453 instant += MICROSECONDS.toNanos(micros); 454 events.add(caption + String.format("%3.2f", (micros / 1000000.0))); 455 } 456 457 @Override sleepMicrosUninterruptibly(long micros)458 void sleepMicrosUninterruptibly(long micros) { 459 sleepMicros("R", micros); 460 } 461 readEventsAndClear()462 String readEventsAndClear() { 463 try { 464 return events.toString(); 465 } finally { 466 events.clear(); 467 } 468 } 469 470 @Override toString()471 public String toString() { 472 return events.toString(); 473 } 474 } 475 testMocking()476 public void testMocking() throws Exception { 477 RateLimiter mockito = Mockito.mock(RateLimiter.class); 478 RateLimiter easyMock = EasyMock.createNiceMock(RateLimiter.class); 479 EasyMock.replay(easyMock); 480 for (Method method : RateLimiter.class.getMethods()) { 481 if (!isStatic(method.getModifiers()) 482 && !NOT_WORKING_ON_MOCKS.contains(method.getName()) 483 && !method.getDeclaringClass().equals(Object.class)) { 484 method.invoke(mockito, arbitraryParameters(method)); 485 method.invoke(easyMock, arbitraryParameters(method)); 486 } 487 } 488 } 489 arbitraryParameters(Method method)490 private static Object[] arbitraryParameters(Method method) { 491 Class<?>[] parameterTypes = method.getParameterTypes(); 492 Object[] params = new Object[parameterTypes.length]; 493 for (int i = 0; i < parameterTypes.length; i++) { 494 params[i] = PARAMETER_VALUES.get(parameterTypes[i]); 495 } 496 return params; 497 } 498 499 private static final ImmutableSet<String> NOT_WORKING_ON_MOCKS = 500 ImmutableSet.of("latestPermitAgeSec", "setRate"); 501 502 // We would use ArbitraryInstances, but it returns 0, invalid for many RateLimiter methods. 503 private static final ImmutableClassToInstanceMap<Object> PARAMETER_VALUES = 504 ImmutableClassToInstanceMap.builder() 505 .put(int.class, 1) 506 .put(long.class, 1L) 507 .put(double.class, 1.0) 508 .put(TimeUnit.class, SECONDS) 509 .build(); 510 } 511