Search in sources :

Example 6 with ConstantQpsRateLimiter

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

the class TestConstantQpsRateLimiter method lowNonWholeRate.

@Test(timeOut = TEST_TIMEOUT)
public void lowNonWholeRate() {
    for (int i = 0; i < TEST_NUM_CYCLES; i++) {
        ClockedExecutor executor = new ClockedExecutor();
        ClockedExecutor circularBufferExecutor = new ClockedExecutor();
        ConstantQpsRateLimiter rateLimiter = new ConstantQpsRateLimiter(executor, executor, executor, TestEvictingCircularBuffer.getBuffer(circularBufferExecutor));
        rateLimiter.setRate(TEST_LOW_FRACTIONAL_QPS, ONE_SECOND, UNLIMITED_BURST);
        rateLimiter.setBufferCapacity(1);
        TattlingCallback<None> tattler = new TattlingCallback<>(executor);
        rateLimiter.submit(tattler);
        // run for enough time such that 3 queries are sent
        executor.runFor((int) (((ONE_SECOND / TEST_LOW_FRACTIONAL_QPS) * 3) - 1));
        Assert.assertTrue(tattler.getInteractCount() == 3);
    }
}
Also used : ConstantQpsRateLimiter(com.linkedin.r2.transport.http.client.ConstantQpsRateLimiter) ClockedExecutor(com.linkedin.test.util.ClockedExecutor) None(com.linkedin.common.util.None) Test(org.testng.annotations.Test)

Example 7 with ConstantQpsRateLimiter

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

the class TestConstantQpsDarkClusterStrategy method testStrategy.

@Test(dataProvider = "qpsKeys")
public void testStrategy(int duration, float inboundQps, float outboundQps, int numSourceInstances, int numDarkInstances) {
    IntStream.of(1, 1000, 1000000).forEach(capacity -> {
        DarkClusterDispatcher darkClusterDispatcher = new DefaultDarkClusterDispatcher(new MockClient(false));
        ClockedExecutor executor = new ClockedExecutor();
        Supplier<ConstantQpsRateLimiter> uniqueRateLimiterSupplier = () -> {
            EvictingCircularBuffer uniqueBuffer = TestConstantQpsDarkClusterStrategy.getBuffer(executor);
            ConstantQpsRateLimiter limiter = new ConstantQpsRateLimiter(executor, executor, executor, uniqueBuffer);
            limiter.setBufferCapacity(capacity);
            limiter.setBufferTtl(Integer.MAX_VALUE, ChronoUnit.DAYS);
            return limiter;
        };
        ConstantQpsRateLimiter sharedRateLimiter = uniqueRateLimiterSupplier.get();
        Supplier<ConstantQpsRateLimiter> sharedRateLimiterSupplier = () -> sharedRateLimiter;
        MockClusterInfoProvider mockClusterInfoProvider = new MockClusterInfoProvider();
        mockClusterInfoProvider.putHttpsClusterCount(SOURCE_CLUSTER_NAME, numSourceInstances);
        // dark cluster 1
        BaseDarkClusterDispatcherImpl baseDispatcherOne = new BaseDarkClusterDispatcherImpl(DARK_CLUSTER_NAME_ONE, darkClusterDispatcher, new DoNothingNotifier(), new CountingVerifierManager());
        mockClusterInfoProvider.putHttpsClusterCount(DARK_CLUSTER_NAME_ONE, numDarkInstances);
        ConstantQpsRateLimiter rateLimiterOne = sharedRateLimiterSupplier.get();
        // dark cluster 2
        BaseDarkClusterDispatcherImpl baseDispatcherTwo = new BaseDarkClusterDispatcherImpl(DARK_CLUSTER_NAME_TWO, darkClusterDispatcher, new DoNothingNotifier(), new CountingVerifierManager());
        mockClusterInfoProvider.putHttpsClusterCount(DARK_CLUSTER_NAME_TWO, numDarkInstances);
        ConstantQpsRateLimiter rateLimiterTwo = sharedRateLimiterSupplier.get();
        // dark cluster 3
        BaseDarkClusterDispatcherImpl baseDispatcherThree = new BaseDarkClusterDispatcherImpl(DARK_CLUSTER_NAME_THREE, darkClusterDispatcher, new DoNothingNotifier(), new CountingVerifierManager());
        mockClusterInfoProvider.putHttpsClusterCount(DARK_CLUSTER_NAME_THREE, numDarkInstances);
        ConstantQpsRateLimiter rateLimiterThree = uniqueRateLimiterSupplier.get();
        List<ConstantQpsDarkClusterStrategy> strategies = new ArrayList<>();
        strategies.add(new ConstantQpsDarkClusterStrategy(SOURCE_CLUSTER_NAME, DARK_CLUSTER_NAME_ONE, outboundQps, baseDispatcherOne, new DoNothingNotifier(), mockClusterInfoProvider, rateLimiterOne));
        strategies.add(new ConstantQpsDarkClusterStrategy(SOURCE_CLUSTER_NAME, DARK_CLUSTER_NAME_TWO, outboundQps, baseDispatcherTwo, new DoNothingNotifier(), mockClusterInfoProvider, rateLimiterTwo));
        strategies.add(new ConstantQpsDarkClusterStrategy(SOURCE_CLUSTER_NAME, DARK_CLUSTER_NAME_THREE, outboundQps, baseDispatcherThree, new DoNothingNotifier(), mockClusterInfoProvider, rateLimiterThree));
        // simulate receiving the configured qps while dispatching over the duration
        int msBetweenEachInboundRequest = (int) (1000 / inboundQps);
        for (int runTime = 0; runTime < duration; runTime = runTime + msBetweenEachInboundRequest) {
            RestRequest dummyRestRequest = new RestRequestBuilder(URI.create("foo")).build();
            for (ConstantQpsDarkClusterStrategy strategy : strategies) {
                strategy.handleRequest(dummyRestRequest, dummyRestRequest, new RequestContext());
            }
            executor.runFor(msBetweenEachInboundRequest);
        }
        double validation = ((duration == 0 ? 0 : duration / 1000.0) * outboundQps * numDarkInstances) / (double) (numSourceInstances);
        // Cluster One and Two share a rate limiter, so their combined QPS should match the expected value.
        int actualCountClusterOne = baseDispatcherOne.getRequestCount();
        int actualCountClusterTwo = baseDispatcherTwo.getRequestCount();
        int expectedCountClusterOneAndTwo = (int) validation;
        Assert.assertEquals(actualCountClusterOne + actualCountClusterTwo, expectedCountClusterOneAndTwo, expectedCountClusterOneAndTwo * ERR_PCT, "count not within expected range");
        // Cluster Three uses its own so it matches the expected value on its own.
        int expectedCountClusterThree = (int) validation;
        int actualCountClusterThree = baseDispatcherThree.getRequestCount();
        Assert.assertEquals(actualCountClusterThree, expectedCountClusterThree, expectedCountClusterThree * ERR_PCT, "count not within expected range");
    });
}
Also used : DefaultDarkClusterDispatcher(com.linkedin.darkcluster.impl.DefaultDarkClusterDispatcher) DarkClusterDispatcher(com.linkedin.darkcluster.api.DarkClusterDispatcher) DefaultDarkClusterDispatcher(com.linkedin.darkcluster.impl.DefaultDarkClusterDispatcher) ArrayList(java.util.ArrayList) ClockedExecutor(com.linkedin.test.util.ClockedExecutor) ConstantQpsRateLimiter(com.linkedin.r2.transport.http.client.ConstantQpsRateLimiter) EvictingCircularBuffer(com.linkedin.r2.transport.http.client.EvictingCircularBuffer) RestRequest(com.linkedin.r2.message.rest.RestRequest) RestRequestBuilder(com.linkedin.r2.message.rest.RestRequestBuilder) RequestContext(com.linkedin.r2.message.RequestContext) ConstantQpsDarkClusterStrategy(com.linkedin.darkcluster.impl.ConstantQpsDarkClusterStrategy) BaseDarkClusterDispatcherImpl(com.linkedin.darkcluster.impl.BaseDarkClusterDispatcherImpl) Test(org.testng.annotations.Test)

Example 8 with ConstantQpsRateLimiter

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

the class DarkClusterStrategyFactoryImpl method createStrategy.

/**
 * In the future, additional strategies can be added, and the logic here can choose the appropriate one based on the config values.
 */
private DarkClusterStrategy createStrategy(String darkClusterName, DarkClusterConfig darkClusterConfig) {
    if (darkClusterConfig.hasDarkClusterStrategyPrioritizedList()) {
        DarkClusterStrategyNameArray strategyList = darkClusterConfig.getDarkClusterStrategyPrioritizedList();
        for (com.linkedin.d2.DarkClusterStrategyName darkClusterStrategyName : strategyList) {
            switch(darkClusterStrategyName) {
                case RELATIVE_TRAFFIC:
                    if (RelativeTrafficMultiplierDarkClusterStrategy.isValidConfig(darkClusterConfig)) {
                        BaseDarkClusterDispatcher baseDarkClusterDispatcher = new BaseDarkClusterDispatcherImpl(darkClusterName, _darkClusterDispatcher, _notifier, _verifierManager);
                        return new RelativeTrafficMultiplierDarkClusterStrategy(_sourceClusterName, darkClusterName, darkClusterConfig.getMultiplier(), baseDarkClusterDispatcher, _notifier, _facilities.getClusterInfoProvider(), _random);
                    }
                    break;
                case IDENTICAL_TRAFFIC:
                    if (IdenticalTrafficMultiplierDarkClusterStrategy.isValidConfig(darkClusterConfig)) {
                        BaseDarkClusterDispatcher baseDarkClusterDispatcher = new BaseDarkClusterDispatcherImpl(darkClusterName, _darkClusterDispatcher, _notifier, _verifierManager);
                        return new IdenticalTrafficMultiplierDarkClusterStrategy(_sourceClusterName, darkClusterName, darkClusterConfig.getMultiplier(), baseDarkClusterDispatcher, _notifier, _facilities.getClusterInfoProvider(), _random);
                    }
                    break;
                case CONSTANT_QPS:
                    if (_rateLimiterSupplier == null) {
                        LOG.error("Dark Cluster {} configured to use CONSTANT_QPS strategy, but no rate limiter provided during instantiation. " + "No Dark Cluster strategy will be used!", darkClusterName);
                        break;
                    }
                    if (ConstantQpsDarkClusterStrategy.isValidConfig(darkClusterConfig)) {
                        BaseDarkClusterDispatcher baseDarkClusterDispatcher = new BaseDarkClusterDispatcherImpl(darkClusterName, _darkClusterDispatcher, _notifier, _verifierManager);
                        ConstantQpsRateLimiter rateLimiter = _rateLimiterSupplier.get();
                        rateLimiter.setBufferCapacity(darkClusterConfig.getDispatcherMaxRequestsToBuffer());
                        rateLimiter.setBufferTtl(darkClusterConfig.getDispatcherBufferedRequestExpiryInSeconds(), ChronoUnit.SECONDS);
                        return new ConstantQpsDarkClusterStrategy(_sourceClusterName, darkClusterName, darkClusterConfig.getDispatcherOutboundTargetRate(), baseDarkClusterDispatcher, _notifier, _facilities.getClusterInfoProvider(), rateLimiter);
                    }
                    break;
                default:
                    break;
            }
        }
    }
    return new NoOpDarkClusterStrategy();
}
Also used : ConstantQpsRateLimiter(com.linkedin.r2.transport.http.client.ConstantQpsRateLimiter) DarkClusterStrategyNameArray(com.linkedin.d2.DarkClusterStrategyNameArray) BaseDarkClusterDispatcher(com.linkedin.darkcluster.api.BaseDarkClusterDispatcher) NoOpDarkClusterStrategy(com.linkedin.darkcluster.api.NoOpDarkClusterStrategy)

Aggregations

ConstantQpsRateLimiter (com.linkedin.r2.transport.http.client.ConstantQpsRateLimiter)8 ClockedExecutor (com.linkedin.test.util.ClockedExecutor)7 Test (org.testng.annotations.Test)6 None (com.linkedin.common.util.None)5 ArrayList (java.util.ArrayList)3 DarkClusterDispatcher (com.linkedin.darkcluster.api.DarkClusterDispatcher)2 DefaultDarkClusterDispatcher (com.linkedin.darkcluster.impl.DefaultDarkClusterDispatcher)2 DarkClusterConfig (com.linkedin.d2.DarkClusterConfig)1 DarkClusterStrategyNameArray (com.linkedin.d2.DarkClusterStrategyNameArray)1 Facilities (com.linkedin.d2.balancer.Facilities)1 BaseDarkClusterDispatcher (com.linkedin.darkcluster.api.BaseDarkClusterDispatcher)1 NoOpDarkClusterStrategy (com.linkedin.darkcluster.api.NoOpDarkClusterStrategy)1 BaseDarkClusterDispatcherImpl (com.linkedin.darkcluster.impl.BaseDarkClusterDispatcherImpl)1 ConstantQpsDarkClusterStrategy (com.linkedin.darkcluster.impl.ConstantQpsDarkClusterStrategy)1 DarkClusterStrategyFactoryImpl (com.linkedin.darkcluster.impl.DarkClusterStrategyFactoryImpl)1 RequestContext (com.linkedin.r2.message.RequestContext)1 RestRequest (com.linkedin.r2.message.rest.RestRequest)1 RestRequestBuilder (com.linkedin.r2.message.rest.RestRequestBuilder)1 EvictingCircularBuffer (com.linkedin.r2.transport.http.client.EvictingCircularBuffer)1 HashSet (java.util.HashSet)1