use of com.linkedin.r2.transport.http.client.AsyncPool in project rest.li by linkedin.
the class ChannelPoolHandler method channelRead.
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
AsyncPool<Channel> pool = ctx.channel().attr(CHANNEL_POOL_ATTR_KEY).getAndRemove();
if (pool != null) {
RestResponse restResponse = (RestResponse) msg;
List<String> connectionTokens = restResponse.getHeaderValues("connection");
if (connectionTokens != null) {
for (String token : connectionTokens) {
if ("close".equalsIgnoreCase(token)) {
pool.dispose(ctx.channel());
return;
}
}
}
pool.put(ctx.channel());
}
}
use of com.linkedin.r2.transport.http.client.AsyncPool in project rest.li by linkedin.
the class Http2NettyStreamClient method doWriteRequest.
@Override
protected void doWriteRequest(Request request, final RequestContext context, SocketAddress address, TimeoutTransportCallback<StreamResponse> callback) {
final AsyncPool<Channel> pool;
try {
pool = _channelPoolManager.getPoolForAddress(address);
} catch (IllegalStateException e) {
errorResponse(callback, e);
return;
}
context.putLocalAttr(R2Constants.HTTP_PROTOCOL_VERSION, HttpProtocolVersion.HTTP_2);
Callback<Channel> getCallback = new ChannelPoolGetCallback(pool, request, callback);
final Cancellable pendingGet = pool.get(getCallback);
if (pendingGet != null) {
callback.addTimeoutTask(() -> pendingGet.cancel());
}
}
use of com.linkedin.r2.transport.http.client.AsyncPool in project rest.li by linkedin.
the class TestAsyncPool method testCreationTimeout.
@Test(dataProvider = "creationTimeoutDataProvider")
public void testCreationTimeout(int poolSize, int concurrency) throws Exception {
// this object creation life cycle simulate the creation limbo state
ObjectCreatorThatNeverCreates objectCreatorThatNeverCreates = new ObjectCreatorThatNeverCreates();
ClockedExecutor clockedExecutor = new ClockedExecutor();
ExponentialBackOffRateLimiter rateLimiter = new ExponentialBackOffRateLimiter(0, 5000, 10, clockedExecutor, concurrency);
final AsyncPool<Object> pool = new AsyncPoolImpl<>("object pool", objectCreatorThatNeverCreates, poolSize, Integer.MAX_VALUE, Integer.MAX_VALUE, clockedExecutor, Integer.MAX_VALUE, AsyncPoolImpl.Strategy.MRU, 0, rateLimiter, clockedExecutor, new LongTracking());
pool.start();
List<FutureCallback<Object>> checkoutCallbacks = new ArrayList<>();
// Lets try to checkout more than the max Pool Size times when the object creator is in limbo state
for (int i = 0; i < poolSize * 2; i++) {
FutureCallback<Object> cb = new FutureCallback<>();
checkoutCallbacks.add(cb);
// Reset the exponential back off due to creation timeout error
rateLimiter.setPeriod(0);
pool.get(cb);
// run for the duration of default creation timeout
// TODO: parameterize the creation duration when the default creation gets parameterized
clockedExecutor.runFor(AsyncPoolImpl.DEFAULT_OBJECT_CREATION_TIMEOUT);
}
// drain all the pending tasks
clockedExecutor.runFor(AsyncPoolImpl.DEFAULT_OBJECT_CREATION_TIMEOUT);
// since the object creator went to limbo state
for (FutureCallback<Object> cb : checkoutCallbacks) {
try {
cb.get(100, TimeUnit.MILLISECONDS);
} catch (Exception ex) {
Assert.assertTrue(ex.getCause() instanceof ObjectCreationTimeoutException);
}
}
// Lets make sure the channel pool stats are at expected state
PoolStats stats = pool.getStats();
// Lets make sure all the limbo creations are timed out as expected
Assert.assertEquals(stats.getTotalCreateErrors(), poolSize * 2);
// No checkout should have happened due to object creator in limbo
Assert.assertEquals(stats.getCheckedOut(), 0);
// No Idle objects in the pool
Assert.assertEquals(stats.getIdleCount(), 0);
// Lets make sure that all the slots in the pool are reclaimed even if the object creation is in limbo
Assert.assertEquals(stats.getPoolSize(), 0);
// Since the max pending creation request reached the max pool size,
// we should have reached the maPool Size at least once
Assert.assertEquals(stats.getMaxPoolSize(), poolSize);
// Since no object is successfully created, expecting idle objects to be zero
Assert.assertEquals(stats.getIdleCount(), 0);
}
use of com.linkedin.r2.transport.http.client.AsyncPool in project rest.li by linkedin.
the class TestAsyncPool method testWaiterTimeout.
/**
* This test case verifies that the correct number of waiters are timed out while waiting for object from the pool
*
* 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, O 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, P number of objects are returned to the pool which are created in Phase A, this will make
* the number of waiter queue size to be O-P
* D = In Phase D, A delay will be introduced to timeout the waiters and all the O-P waiters should be timed out.
* After the delay the object creation will be unblocked and it should create aleast the concurrency number of
* objects even though the waiters are timedout.
*
* @param numberOfCheckoutsInPhaseA the N number of checkout operations that will be performed in phase A
* @param numberOfCheckoutsInPhaseB the O number of checkout operations that will be performed in Phase B
* @param numbOfObjectsToBeReturnedInPhaseC the numeber of objects returned in Phase C
* @param poolSize size of the pool,
* @param concurrency concurrency of the rate limiter
*/
@Test(dataProvider = "waiterTimeoutDataProvider")
public void testWaiterTimeout(int numberOfCheckoutsInPhaseA, int numberOfCheckoutsInPhaseB, int numbOfObjectsToBeReturnedInPhaseC, int poolSize, int concurrency, int waiterTimeout) throws Exception {
CreationBlockableSynchronousLifecycle blockableObjectCreator = new CreationBlockableSynchronousLifecycle(numberOfCheckoutsInPhaseB, concurrency);
ScheduledExecutorService executor = Executors.newScheduledThreadPool(500);
ExponentialBackOffRateLimiter rateLimiter = new ExponentialBackOffRateLimiter(0, 5000, 10, executor, concurrency);
ClockedExecutor clockedExecutor = new ClockedExecutor();
final AsyncPool<Object> pool = new AsyncPoolImpl<>("object pool", blockableObjectCreator, poolSize, Integer.MAX_VALUE, waiterTimeout, clockedExecutor, Integer.MAX_VALUE, AsyncPoolImpl.Strategy.MRU, 0, rateLimiter, clockedExecutor, new LongTracking());
pool.start();
// Phase A : Checking out object 'numberOfCheckoutsInPhaseA' times !
List<Object> checkedOutObjects = performCheckout(numberOfCheckoutsInPhaseA, pool);
// Phase B : Blocking object creation and performing the checkout 'numberOfCheckoutsInPhaseB' times again
blockableObjectCreator.blockCreation();
Future<None> future = performUnblockingCheckout(numberOfCheckoutsInPhaseB, 0, pool);
blockableObjectCreator.waitUntilAllBlocked();
// Phase C : Returning the checkedOut objects from Phase A back to the object pool
for (int i = 0; i < numbOfObjectsToBeReturnedInPhaseC; i++) {
pool.put(checkedOutObjects.remove(0));
}
clockedExecutor.runFor(waiterTimeout);
// Phase D : All the object creation in phase B gets unblocked now
blockableObjectCreator.unblockCreation();
try {
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"));
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);
}
PoolStats stats = pool.getStats();
Assert.assertEquals(stats.getTotalCreationIgnored(), numberOfCheckoutsInPhaseB - concurrency);
Assert.assertEquals(stats.getCheckedOut(), numberOfCheckoutsInPhaseA);
Assert.assertEquals(stats.getIdleCount(), concurrency);
Assert.assertEquals(stats.getTotalCreated(), numberOfCheckoutsInPhaseA + concurrency);
Assert.assertEquals(stats.getPoolSize(), numberOfCheckoutsInPhaseA + concurrency);
Assert.assertEquals(stats.getTotalWaiterTimedOut(), numberOfCheckoutsInPhaseB - numbOfObjectsToBeReturnedInPhaseC);
}
use of com.linkedin.r2.transport.http.client.AsyncPool in project rest.li by linkedin.
the class HttpNettyClient method sendRequest.
/**
* Sends the request to the {@link ChannelPipeline}.
*/
private void sendRequest(Request request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<StreamResponse> callback) {
final TransportCallback<StreamResponse> decoratedCallback = decorateUserCallback(request, callback);
final NettyClientState state = _state.get();
if (state != NettyClientState.RUNNING) {
decoratedCallback.onResponse(TransportResponseImpl.error(new IllegalStateException("Client is not running")));
return;
}
final long resolvedRequestTimeout = resolveRequestTimeout(requestContext, _requestTimeout);
// Timeout ensures the request callback is always invoked and is cancelled before the
// responsibility of invoking the callback is handed over to the pipeline.
final Timeout<None> timeout = new Timeout<>(_scheduler, resolvedRequestTimeout, TimeUnit.MILLISECONDS, None.none());
timeout.addTimeoutTask(() -> decoratedCallback.onResponse(TransportResponseImpl.error(new TimeoutException("Exceeded request timeout of " + resolvedRequestTimeout + "ms"))));
// resolve address
final SocketAddress address;
try {
TimingContextUtil.markTiming(requestContext, TIMING_KEY);
address = resolveAddress(request, requestContext);
TimingContextUtil.markTiming(requestContext, TIMING_KEY);
} catch (Exception e) {
decoratedCallback.onResponse(TransportResponseImpl.error(e));
return;
}
// Serialize wire attributes
final Request requestWithWireAttrHeaders;
if (request instanceof StreamRequest) {
requestWithWireAttrHeaders = buildRequestWithWireAttributes((StreamRequest) request, wireAttrs);
} else {
MessageType.setMessageType(MessageType.Type.REST, wireAttrs);
requestWithWireAttrHeaders = buildRequestWithWireAttributes((RestRequest) request, wireAttrs);
}
// Gets channel pool
final AsyncPool<Channel> pool;
try {
pool = getChannelPoolManagerPerRequest(requestWithWireAttrHeaders).getPoolForAddress(address);
} catch (IllegalStateException e) {
decoratedCallback.onResponse(TransportResponseImpl.error(e));
return;
}
// Saves protocol version in request context
requestContext.putLocalAttr(R2Constants.HTTP_PROTOCOL_VERSION, _protocolVersion);
final Cancellable pendingGet = pool.get(new ChannelPoolGetCallback(pool, requestWithWireAttrHeaders, requestContext, decoratedCallback, timeout, resolvedRequestTimeout, _streamingTimeout));
if (pendingGet != null) {
timeout.addTimeoutTask(pendingGet::cancel);
}
}
Aggregations