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;
}
}
};
}
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();
}
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);
}
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);
}
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);
}
Aggregations