Search in sources :

Example 16 with AsyncPool

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

the class AsyncPoolImpl method get.

@Override
public Cancellable get(final Callback<T> callback) {
    // getter needs to add to wait queue atomically with check for empty pool
    // putter needs to add to pool atomically with check for empty wait queue
    boolean create = false;
    boolean reject = false;
    final LinkedDeque.Node<Callback<T>> node;
    Callback<T> callbackWithTracking = new TimeTrackingCallback<>(callback);
    for (; ; ) {
        TimedObject<T> obj = null;
        final State state;
        synchronized (_lock) {
            state = _state;
            if (state == State.RUNNING) {
                if (_strategy == Strategy.LRU) {
                    obj = _idle.pollFirst();
                } else {
                    obj = _idle.pollLast();
                }
                if (obj == null) {
                    if (_waiters.size() < _maxWaiters) {
                        if (isWaiterTimeoutEnabled()) {
                            callbackWithTracking = new WaiterTimeoutCallback(callbackWithTracking);
                        }
                        // No objects available and the waiter list is not full; add to waiter list and break out of loop
                        node = _waiters.addLastNode(callbackWithTracking);
                        create = shouldCreate();
                    } else {
                        reject = true;
                        node = null;
                    }
                    break;
                }
            }
        }
        if (state != State.RUNNING) {
            // Defer execution of the callback until we are out of the synchronized block
            callbackWithTracking.onError(new IllegalStateException(_poolName + " is " + _state));
            return () -> false;
        }
        T rawObj = obj.get();
        if (_lifecycle.validateGet(rawObj)) {
            trc("dequeued an idle object");
            // Valid object; done
            synchronized (_lock) {
                _checkedOut++;
                _statsTracker.sampleMaxCheckedOut();
            }
            callbackWithTracking.onSuccess(rawObj);
            return () -> false;
        }
        // Invalid object, discard it and keep trying
        destroy(rawObj, true);
        trc("dequeued and disposed an invalid idle object");
    }
    if (reject) {
        // This is a recoverable exception. User can simply retry the failed get() operation.
        callbackWithTracking.onError(new SizeLimitExceededException("AsyncPool " + _poolName + " reached maximum waiter size: " + _maxWaiters));
        return () -> false;
    }
    trc("enqueued a waiter");
    if (create) {
        create();
    }
    return new Cancellable() {

        @Override
        public boolean cancel() {
            synchronized (_lock) {
                boolean cancelled = _waiters.removeNode(node) != null;
                if (cancelled) {
                    shutdownIfNeeded();
                }
                return cancelled;
            }
        }
    };
}
Also used : Cancellable(com.linkedin.r2.util.Cancellable) SizeLimitExceededException(com.linkedin.r2.SizeLimitExceededException) Callback(com.linkedin.common.callback.Callback) SimpleCallback(com.linkedin.common.callback.SimpleCallback) LinkedDeque(com.linkedin.r2.util.LinkedDeque)

Example 17 with AsyncPool

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

the class TestHttpsEarlyHandshake method testHttpsEarlyHandshakeHttp1.

@Test
public void testHttpsEarlyHandshakeHttp1() throws Exception {
    EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    ChannelPoolManagerFactoryImpl channelPoolManagerFactory = new ChannelPoolManagerFactoryImpl(eventLoopGroup, scheduler, SSL_SESSION_RESUMPTION_ENABLED, _clientProvider.getUsePipelineV2(), HttpClientFactory.DEFAULT_CHANNELPOOL_WAITER_TIMEOUT, HttpClientFactory.DEFAULT_CONNECT_TIMEOUT, HttpClientFactory.DEFAULT_SSL_HANDSHAKE_TIMEOUT);
    SSLContext context = SslContextUtil.getContext();
    ChannelPoolManagerKey key = new ChannelPoolManagerKeyBuilder().setMinPoolSize(1).setSSLContext(context).setSSLParameters(context.getDefaultSSLParameters()).build();
    ChannelPoolManager channelPoolManager = channelPoolManagerFactory.buildRest(key);
    InetAddress inetAddress = InetAddress.getByName("localhost");
    final SocketAddress address = new InetSocketAddress(inetAddress, _port);
    // get the channel, when it is returned it might not be active yet
    FutureCallback<Channel> futureCallback = new FutureCallback<>();
    AsyncPool<Channel> poolForAddress = channelPoolManager.getPoolForAddress(address);
    poolForAddress.get(futureCallback);
    final Channel channel = futureCallback.get(5, TimeUnit.SECONDS);
    // wait until it gets active
    FutureCallback<Future<? super Void>> futureActiveCallback = new FutureCallback<>();
    channel.newSucceededFuture().addListener(futureActiveCallback::onSuccess);
    futureActiveCallback.get(5, TimeUnit.SECONDS);
    // retrieve the ssl handler from the pipeline and wait till the handshake happens
    SslHandler sslHandler = (SslHandler) channel.pipeline().get(SslHandlerUtil.PIPELINE_SSL_HANDLER);
    FutureCallback<Future<? super Channel>> futureHandshakeCallback = new FutureCallback<>();
    sslHandler.handshakeFuture().addListener(f -> {
        if (f.isSuccess()) {
            futureHandshakeCallback.onSuccess(f);
        } else {
            futureHandshakeCallback.onError(f.cause());
        }
    });
    futureHandshakeCallback.get(5, TimeUnit.SECONDS).get(5, TimeUnit.SECONDS);
    poolForAddress.dispose(channel);
    // shutdown the pool
    FutureCallback<None> futureShutdownCallback = new FutureCallback<>();
    channelPoolManager.shutdown(futureShutdownCallback, () -> {
    }, () -> {
    }, 5000);
    futureShutdownCallback.get(5, TimeUnit.SECONDS);
    // shutdown the client executors
    scheduler.shutdown();
    eventLoopGroup.shutdownGracefully();
}
Also used : ChannelPoolManagerKeyBuilder(com.linkedin.r2.transport.http.client.common.ChannelPoolManagerKeyBuilder) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) ChannelPoolManagerFactoryImpl(com.linkedin.r2.transport.http.client.common.ChannelPoolManagerFactoryImpl) InetSocketAddress(java.net.InetSocketAddress) ChannelPoolManager(com.linkedin.r2.transport.http.client.common.ChannelPoolManager) Channel(io.netty.channel.Channel) SSLContext(javax.net.ssl.SSLContext) SslHandler(io.netty.handler.ssl.SslHandler) EventLoopGroup(io.netty.channel.EventLoopGroup) NioEventLoopGroup(io.netty.channel.nio.NioEventLoopGroup) Future(io.netty.util.concurrent.Future) ChannelPoolManagerKey(com.linkedin.r2.transport.http.client.common.ChannelPoolManagerKey) SocketAddress(java.net.SocketAddress) InetSocketAddress(java.net.InetSocketAddress) InetAddress(java.net.InetAddress) None(com.linkedin.common.util.None) NioEventLoopGroup(io.netty.channel.nio.NioEventLoopGroup) FutureCallback(com.linkedin.common.callback.FutureCallback) AbstractEchoServiceTest(test.r2.integ.clientserver.providers.AbstractEchoServiceTest) Test(org.testng.annotations.Test)

Example 18 with AsyncPool

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

the class TestAsyncPool method performUnblockingCheckout.

private Future<None> performUnblockingCheckout(int numberOfCheckoutRequests, int numberOfCheckouts, AsyncPool<Object> pool) {
    ScheduledExecutorService checkoutExecutor = Executors.newScheduledThreadPool(500);
    CountDownLatch checkoutLatch = new CountDownLatch(numberOfCheckouts);
    CountDownLatch requestLatch = new CountDownLatch(numberOfCheckoutRequests);
    Runnable checkoutTask = getCheckoutTask(pool, new LinkedList<>(), new Object(), checkoutLatch, requestLatch);
    for (int i = 0; i < numberOfCheckoutRequests; i++) {
        checkoutExecutor.execute(checkoutTask);
    }
    try {
        requestLatch.await(5, TimeUnit.SECONDS);
    } catch (Exception ex) {
        Assert.fail("Too long to perform checkout operation");
    }
    return new DelayedFutureCallback<>(checkoutLatch, checkoutExecutor);
}
Also used : ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) CountDownLatch(java.util.concurrent.CountDownLatch) ObjectCreationTimeoutException(com.linkedin.r2.transport.http.client.ObjectCreationTimeoutException) TimeoutException(java.util.concurrent.TimeoutException) ExecutionException(java.util.concurrent.ExecutionException)

Example 19 with AsyncPool

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

the class TestAsyncPool method testWaitTimeStats.

/**
 * Wait time percentile, average, and maximum tracking is deprecated in {@link AsyncPool} implementations.
 */
@Test
public void testWaitTimeStats() throws Exception {
    final int POOL_SIZE = 25;
    final int CHECKOUT = POOL_SIZE;
    final long DELAY = 100;
    final double DELTA = 0.1;
    DelayedLifecycle lifecycle = new DelayedLifecycle(DELAY);
    final AsyncPool<Object> pool = new AsyncPoolImpl<>("object pool", lifecycle, POOL_SIZE, 100, _executor);
    pool.start();
    PoolStats stats;
    List<Object> objects = new ArrayList<>(CHECKOUT);
    for (int i = 0; i < CHECKOUT; i++) {
        FutureCallback<Object> cb = new FutureCallback<>();
        pool.get(cb);
        Object o = cb.get();
        objects.add(o);
    }
    stats = pool.getStats();
    Assert.assertEquals(stats.getWaitTimeAvg(), DELAY, DELTA * DELAY);
}
Also used : AsyncPoolImpl(com.linkedin.r2.transport.http.client.AsyncPoolImpl) ArrayList(java.util.ArrayList) FutureCallback(com.linkedin.common.callback.FutureCallback) PoolStats(com.linkedin.r2.transport.http.client.PoolStats) Test(org.testng.annotations.Test)

Example 20 with AsyncPool

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

the class TestAsyncPool method testObjectsAreNotCreatedWhenThereAreNoWaiters.

/**
 * This test case verifies that if more request object creation requests are submitted to the rate limiter, it only
 * creates the absolute required maximum (see below example)
 *
 *  Assumption: the channel pool max size is always bigger than the requested checkout size
 *
 *|----------A------------|---------------B---------------|---------------C--------------|-------------D--------------
 *  A = In Phase A , N number of object checkout request to the pool when there are no tasks pending in the
 *      rate limiter. A's Expected result = channel pool will create N number of new objects and check them out
 *  B = In Phase B, N number of object checkout request again sent to the channel pool when the pool has already
 *      checkout N number of objects, In this phase, the object creation inside the pool is blocked and the
 *      rate limiter will Queue the creation requests once it reached its maximum concurrency configured.
 *  C = Ih Phase C, N number of objects are returned to the pool which are created in Phase A, this will make
 *      the number of idle objects in the pool as N.
 *  D = In Phase D, All the object creation blocked in Phase B will get un blocked and create number of new objects
 *      that are equal to the rate limiter concurrency. When rate limiter executes the queued creation requests - it
 *      should ignore the creation requests as there are no object waiters in the pool and thus effectively only
 *      creating the absolute minimum required count (N+Concurrency)
 *
 * @param numberOfCheckouts the N number of checkout operations that will be performed in phase A & B
 * @param poolSize the maximum Object Pool Size
 * @param concurrency the maximum allowed concurrent object creation
 */
@Test(dataProvider = "channelStateRandomDataProvider")
public void testObjectsAreNotCreatedWhenThereAreNoWaiters(int numberOfCheckouts, int poolSize, int concurrency) throws Exception {
    CreationBlockableSynchronousLifecycle blockableObjectCreator = new CreationBlockableSynchronousLifecycle(numberOfCheckouts, concurrency);
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(500);
    ExponentialBackOffRateLimiter rateLimiter = new ExponentialBackOffRateLimiter(0, 5000, 10, executor, concurrency);
    final AsyncPool<Object> pool = new AsyncPoolImpl<>("object pool", blockableObjectCreator, poolSize, Integer.MAX_VALUE, _executor, Integer.MAX_VALUE, AsyncPoolImpl.Strategy.MRU, 0, rateLimiter);
    pool.start();
    // Phase A:Checking out object 'numberOfCheckout' times!
    List<Object> checkedOutObjects = performCheckout(numberOfCheckouts, pool);
    // Phase B:Blocking object creation and performing the checkout 'numberOfCheckout' times again
    blockableObjectCreator.blockCreation();
    Future<None> future = performUnblockingCheckout(numberOfCheckouts, numberOfCheckouts, pool);
    blockableObjectCreator.waitUntilAllBlocked();
    // Phase C:Returning the checkedOut objects from Phase A back to the object pool
    for (Object checkedOutObject : checkedOutObjects) {
        pool.put(checkedOutObject);
    }
    // Phase D:All the object creation in phase B gets unblocked now
    blockableObjectCreator.unblockCreation();
    try {
        // Wait for all object creation to be unblocked
        future.get(5, TimeUnit.SECONDS);
    } catch (Exception e) {
        Assert.fail("Did not complete unblocked object creations on time, Unexpected interruption", e);
    }
    // Making sure the rate limiter pending tasks are submitted to the executor
    AssertionMethods.assertWithTimeout(5000, () -> Assert.assertEquals(rateLimiter.numberOfPendingTasks(), 0, "Number of tasks has to drop to 0"));
    // Wait for all the tasks in the rate limiter executor to finish
    executor.shutdown();
    try {
        if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
            Assert.fail("Executor took too long to shutdown");
        }
    } catch (Exception ex) {
        Assert.fail("Unexpected interruption while shutting down executor", ex);
    }
    // Verify all the expectations
    PoolStats stats = pool.getStats();
    Assert.assertEquals(stats.getTotalCreationIgnored(), numberOfCheckouts - concurrency);
    Assert.assertEquals(stats.getCheckedOut(), numberOfCheckouts);
    Assert.assertEquals(stats.getIdleCount(), concurrency);
    Assert.assertEquals(stats.getTotalCreated(), numberOfCheckouts + concurrency);
    Assert.assertEquals(stats.getPoolSize(), numberOfCheckouts + concurrency);
    Assert.assertEquals(stats.getTotalDestroyed(), 0);
    Assert.assertEquals(stats.getTotalBadDestroyed(), 0);
    Assert.assertEquals(stats.getTotalTimedOut(), 0);
}
Also used : ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) AsyncPoolImpl(com.linkedin.r2.transport.http.client.AsyncPoolImpl) ExponentialBackOffRateLimiter(com.linkedin.r2.transport.http.client.ExponentialBackOffRateLimiter) None(com.linkedin.common.util.None) ObjectCreationTimeoutException(com.linkedin.r2.transport.http.client.ObjectCreationTimeoutException) TimeoutException(java.util.concurrent.TimeoutException) ExecutionException(java.util.concurrent.ExecutionException) PoolStats(com.linkedin.r2.transport.http.client.PoolStats) Test(org.testng.annotations.Test)

Aggregations

Channel (io.netty.channel.Channel)12 TimeoutException (java.util.concurrent.TimeoutException)10 Test (org.testng.annotations.Test)10 Cancellable (com.linkedin.r2.util.Cancellable)9 FutureCallback (com.linkedin.common.callback.FutureCallback)7 AsyncPoolImpl (com.linkedin.r2.transport.http.client.AsyncPoolImpl)7 ExecutionException (java.util.concurrent.ExecutionException)7 ObjectCreationTimeoutException (com.linkedin.r2.transport.http.client.ObjectCreationTimeoutException)6 PoolStats (com.linkedin.r2.transport.http.client.PoolStats)6 ArrayList (java.util.ArrayList)6 ScheduledExecutorService (java.util.concurrent.ScheduledExecutorService)6 None (com.linkedin.common.util.None)5 LongTracking (com.linkedin.common.stats.LongTracking)4 RestRequest (com.linkedin.r2.message.rest.RestRequest)4 InetSocketAddress (java.net.InetSocketAddress)4 SocketAddress (java.net.SocketAddress)4 RestRequestBuilder (com.linkedin.r2.message.rest.RestRequestBuilder)3 RestResponse (com.linkedin.r2.message.rest.RestResponse)3 ExponentialBackOffRateLimiter (com.linkedin.r2.transport.http.client.ExponentialBackOffRateLimiter)3 NioSocketChannel (io.netty.channel.socket.nio.NioSocketChannel)3