Search in sources :

Example 1 with SmoothRateLimiter

use of com.linkedin.r2.transport.http.client.SmoothRateLimiter in project rest.li by linkedin.

the class TestRampUpRateLimiter method testRampUp.

@Test(dataProvider = "targetRamp", timeOut = TEST_TIMEOUT * 1000)
public void testRampUp(int targetPermitsPerPeriod, float rampUp) {
    boolean useRampUpMethod = false;
    for (int k = 0; k < 2; k++, useRampUpMethod = true) {
        _queue.clear();
        ClockedExecutor clockedExecutor = new ClockedExecutor();
        RampUpRateLimiter rateLimiter = new RampUpRateLimiterImpl(new SmoothRateLimiter(clockedExecutor, clockedExecutor, clockedExecutor, _queue, Integer.MAX_VALUE, SmoothRateLimiter.BufferOverflowMode.DROP, RATE_LIMITER_NAME_TEST), clockedExecutor);
        rateLimiter.setRate(0, 1, MINIMUM_BURST, rampUp);
        rateLimiter.setRate(targetPermitsPerPeriod, ONE_SECOND_PERIOD, MINIMUM_BURST, rampUp);
        if (useRampUpMethod) {
            // issue close to 0 permits to have a successful ramp up afterwards
            rateLimiter.setRate(0, 1, MINIMUM_BURST, rampUp);
            rateLimiter.setRate(targetPermitsPerPeriod, ONE_SECOND_PERIOD, MINIMUM_BURST, rampUp);
        }
        AtomicInteger time = new AtomicInteger(0);
        AtomicInteger count = new AtomicInteger(0);
        List<Integer> completionsPerSecond = new ArrayList<>();
        int secondsToReachTargetState = (int) Math.ceil(targetPermitsPerPeriod / rampUp);
        IntStream.range(0, (int) (rampUp * secondsToReachTargetState * (secondsToReachTargetState + 1))).forEach(i -> {
            rateLimiter.submit(new Callback<None>() {

                @Override
                public void onError(Throwable e) {
                    throw new RuntimeException(e);
                }

                @Override
                public void onSuccess(None result) {
                    // counting how many tasks per second we are receiving.
                    if (clockedExecutor.getCurrentTimeMillis() - time.get() >= ONE_SECOND_PERIOD) {
                        time.set(((int) (clockedExecutor.getCurrentTimeMillis() / 1000) * 1000));
                        completionsPerSecond.add(count.get());
                        count.set(1);
                    } else {
                        count.incrementAndGet();
                    }
                }
            });
        });
        // run the clock only for the exact amount of time that is necessary to reach the stable state
        clockedExecutor.runFor((long) ((secondsToReachTargetState + 2) * 1000));
        long countAboveMaxTarget = 0;
        long countAtTarget = 0;
        long countBelowTarget = 0;
        for (Integer i : completionsPerSecond) {
            if (i > targetPermitsPerPeriod)
                countAboveMaxTarget++;
            if (i == targetPermitsPerPeriod)
                countAtTarget++;
            if (i < targetPermitsPerPeriod)
                countBelowTarget++;
        }
        assertEquals(countAboveMaxTarget, 0, "It should never go above the target QPS");
        assertTrue(countAtTarget > 0, "There should be at least one at the target QPS since it should reach the stable state after a while");
        long actualStepsToTarget = (countBelowTarget + 1) + // we want to account for the first seconds in which no task will return if the rampUp<1
        (rampUp < 1 ? (long) (1 / rampUp) - 1 : 0);
        // using countABelowTarget+1, because the one from the last number to the target is never counted
        assertTrue(actualStepsToTarget >= secondsToReachTargetState * 0.9 && actualStepsToTarget <= Math.ceil(secondsToReachTargetState * 1.1), "There should be at least " + secondsToReachTargetState * 0.9 + " steps to get to the target and no more than " + Math.ceil(secondsToReachTargetState * 1.1) + ". Found: " + actualStepsToTarget + ".");
    }
}
Also used : ArrayList(java.util.ArrayList) ClockedExecutor(com.linkedin.test.util.ClockedExecutor) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) SmoothRateLimiter(com.linkedin.r2.transport.http.client.SmoothRateLimiter) None(com.linkedin.common.util.None) Test(org.testng.annotations.Test)

Example 2 with SmoothRateLimiter

use of com.linkedin.r2.transport.http.client.SmoothRateLimiter in project rest.li by linkedin.

the class TestSmoothRateLimiter method testSubmitExceedsMaxBuffered.

@Test(timeOut = TEST_TIMEOUT)
public void testSubmitExceedsMaxBuffered() {
    SmoothRateLimiter rateLimiter = new SmoothRateLimiter(_scheduledExecutorService, _executor, _clock, _queue, 0, SmoothRateLimiter.BufferOverflowMode.DROP, RATE_LIMITER_NAME_TEST);
    rateLimiter.setRate(ONE_PERMIT_PER_PERIOD, ONE_SECOND_PERIOD, UNLIMITED_BURST);
    FutureCallback<None> callback = new FutureCallback<>();
    try {
        rateLimiter.submit(callback);
    } catch (RejectedExecutionException e) {
        Assert.assertFalse("The tasks should have been rejected and not run", callback.isDone());
        // success, the exception has been thrown as expected!
        return;
    }
    Assert.fail("It should have thrown a RejectedExecutionException");
}
Also used : SmoothRateLimiter(com.linkedin.r2.transport.http.client.SmoothRateLimiter) None(com.linkedin.common.util.None) FutureCallback(com.linkedin.common.callback.FutureCallback) RejectedExecutionException(java.util.concurrent.RejectedExecutionException) Test(org.testng.annotations.Test)

Example 3 with SmoothRateLimiter

use of com.linkedin.r2.transport.http.client.SmoothRateLimiter in project rest.li by linkedin.

the class TestRampUpRateLimiter method testRampDownImmediately.

@Test(timeOut = TEST_TIMEOUT)
public void testRampDownImmediately() {
    ClockedExecutor clockedExecutor = new ClockedExecutor();
    RampUpRateLimiter rateLimiter = new RampUpRateLimiterImpl(new SmoothRateLimiter(clockedExecutor, clockedExecutor, clockedExecutor, _queue, Integer.MAX_VALUE, SmoothRateLimiter.BufferOverflowMode.DROP, RATE_LIMITER_NAME_TEST), clockedExecutor);
    rateLimiter.setRate(1000d, ONE_SECOND_PERIOD, MINIMUM_BURST);
    List<FutureCallback<None>> callbacks = new ArrayList<>();
    IntStream.range(0, 1002).forEach(i -> {
        FutureCallback<None> callback = new FutureCallback<>();
        rateLimiter.submit(callback);
        callbacks.add(callback);
    });
    // -1 because if it passes a full second, the new batch of permits will be issued
    clockedExecutor.runFor(ONE_SECOND_PERIOD - 1);
    IntStream.range(0, 1000).forEach(i -> assertTrue(callbacks.get(i).isDone()));
    IntStream.range(1000, 1002).forEach(i -> assertFalse(callbacks.get(i).isDone(), i + " should not have been executed"));
    rateLimiter.setRate(1, ONE_SECOND_PERIOD, 1, Integer.MAX_VALUE);
    clockedExecutor.runFor(ONE_SECOND_PERIOD);
    IntStream.range(1000, 1001).forEach(i -> assertTrue(callbacks.get(i).isDone()));
    IntStream.range(1001, 1002).forEach(i -> assertFalse(callbacks.get(i).isDone(), i + " should not have been executed"));
    clockedExecutor.runFor(ONE_SECOND_PERIOD);
    IntStream.range(1001, 1002).forEach(i -> assertTrue(callbacks.get(i).isDone()));
}
Also used : SmoothRateLimiter(com.linkedin.r2.transport.http.client.SmoothRateLimiter) ArrayList(java.util.ArrayList) ClockedExecutor(com.linkedin.test.util.ClockedExecutor) None(com.linkedin.common.util.None) FutureCallback(com.linkedin.common.callback.FutureCallback) Test(org.testng.annotations.Test)

Example 4 with SmoothRateLimiter

use of com.linkedin.r2.transport.http.client.SmoothRateLimiter in project rest.li by linkedin.

the class TestSmoothRateLimiter method testSubmitExceedsMaxBufferedButNoReject.

@Test(timeOut = TEST_TIMEOUT)
public void testSubmitExceedsMaxBufferedButNoReject() throws InterruptedException, ExecutionException, TimeoutException {
    SmoothRateLimiter rateLimiter = new SmoothRateLimiter(_scheduledExecutorService, _executor, _clock, _queue, 0, SmoothRateLimiter.BufferOverflowMode.SCHEDULE_WITH_WARNING, RATE_LIMITER_NAME_TEST);
    rateLimiter.setRate(ONE_PERMIT_PER_PERIOD, ONE_SECOND_PERIOD, UNLIMITED_BURST);
    int numberOfTasks = 100;
    FutureCallback<None> callback = new FutureCallback<>();
    Callback<None> callbacks = new MultiCallback(callback, numberOfTasks);
    for (int i = 0; i < numberOfTasks; i++) {
        try {
            rateLimiter.submit(callbacks);
        } catch (RejectedExecutionException e) {
            Assert.fail("It should have just run a task and not throw a RejectedExecutionException");
        }
    }
    callback.get(5, TimeUnit.SECONDS);
    Assert.assertTrue("The tasks should run", callback.isDone());
}
Also used : SmoothRateLimiter(com.linkedin.r2.transport.http.client.SmoothRateLimiter) MultiCallback(com.linkedin.common.callback.MultiCallback) None(com.linkedin.common.util.None) FutureCallback(com.linkedin.common.callback.FutureCallback) RejectedExecutionException(java.util.concurrent.RejectedExecutionException) Test(org.testng.annotations.Test)

Aggregations

None (com.linkedin.common.util.None)4 SmoothRateLimiter (com.linkedin.r2.transport.http.client.SmoothRateLimiter)4 Test (org.testng.annotations.Test)4 FutureCallback (com.linkedin.common.callback.FutureCallback)3 ClockedExecutor (com.linkedin.test.util.ClockedExecutor)2 ArrayList (java.util.ArrayList)2 RejectedExecutionException (java.util.concurrent.RejectedExecutionException)2 MultiCallback (com.linkedin.common.callback.MultiCallback)1 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)1