use of com.linkedin.r2.transport.http.client.AsyncPool in project rest.li by linkedin.
the class HttpNettyStreamClient method doWriteRequest.
@Override
protected void doWriteRequest(Request request, 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_1_1);
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 testGetStats.
@Test(retryAnalyzer = SingleRetry.class)
public void testGetStats() throws Exception {
final int POOL_SIZE = 25;
final int MIN_SIZE = 0;
final int MAX_WAITER_SIZE = Integer.MAX_VALUE;
final SettableClock clock = new SettableClock();
final LongTracking waitTimeTracker = new LongTracking();
final int GET = 20;
final int PUT_GOOD = 2;
final int PUT_BAD = 3;
final int DISPOSE = 4;
final int TIMEOUT = 100;
final int WAITER_TIMEOUT = 200;
final int DELAY = 1200;
final UnreliableLifecycle lifecycle = new UnreliableLifecycle();
final AsyncPool<AtomicBoolean> pool = new AsyncPoolImpl<>("object pool", lifecycle, POOL_SIZE, TIMEOUT, WAITER_TIMEOUT, _executor, MAX_WAITER_SIZE, AsyncPoolImpl.Strategy.MRU, MIN_SIZE, new NoopRateLimiter(), clock, waitTimeTracker);
PoolStats stats;
final List<AtomicBoolean> objects = new ArrayList<>();
pool.start();
// test values at initialization
stats = pool.getStats();
Assert.assertEquals(stats.getTotalCreated(), 0);
Assert.assertEquals(stats.getTotalDestroyed(), 0);
Assert.assertEquals(stats.getTotalCreateErrors(), 0);
Assert.assertEquals(stats.getTotalDestroyErrors(), 0);
Assert.assertEquals(stats.getCheckedOut(), 0);
Assert.assertEquals(stats.getTotalTimedOut(), 0);
Assert.assertEquals(stats.getTotalWaiterTimedOut(), 0);
Assert.assertEquals(stats.getTotalBadDestroyed(), 0);
Assert.assertEquals(stats.getMaxPoolSize(), POOL_SIZE);
Assert.assertEquals(stats.getMinPoolSize(), 0);
Assert.assertEquals(stats.getPoolSize(), 0);
Assert.assertEquals(stats.getSampleMaxCheckedOut(), 0);
Assert.assertEquals(stats.getSampleMaxPoolSize(), 0);
// do a few gets
for (int i = 0; i < GET; i++) {
FutureCallback<AtomicBoolean> cb = new FutureCallback<>();
pool.get(cb);
AtomicBoolean obj = cb.get();
objects.add(obj);
}
clock.addDuration(SAMPLING_DURATION_INCREMENT);
stats = pool.getStats();
Assert.assertEquals(stats.getTotalCreated(), GET);
Assert.assertEquals(stats.getTotalDestroyed(), 0);
Assert.assertEquals(stats.getTotalCreateErrors(), 0);
Assert.assertEquals(stats.getTotalDestroyErrors(), 0);
Assert.assertEquals(stats.getCheckedOut(), GET);
Assert.assertEquals(stats.getTotalTimedOut(), 0);
Assert.assertEquals(stats.getTotalBadDestroyed(), 0);
Assert.assertEquals(stats.getMaxPoolSize(), POOL_SIZE);
Assert.assertEquals(stats.getMinPoolSize(), 0);
Assert.assertEquals(stats.getPoolSize(), GET);
Assert.assertEquals(stats.getSampleMaxCheckedOut(), GET);
Assert.assertEquals(stats.getSampleMaxPoolSize(), GET);
// do some puts with good objects
for (int i = 0; i < PUT_GOOD; i++) {
AtomicBoolean obj = objects.remove(objects.size() - 1);
pool.put(obj);
}
clock.addDuration(SAMPLING_DURATION_INCREMENT);
stats = pool.getStats();
Assert.assertEquals(stats.getTotalCreated(), GET);
Assert.assertEquals(stats.getTotalDestroyed(), 0);
Assert.assertEquals(stats.getTotalCreateErrors(), 0);
Assert.assertEquals(stats.getTotalDestroyErrors(), 0);
Assert.assertEquals(stats.getCheckedOut(), GET - PUT_GOOD);
Assert.assertEquals(stats.getTotalTimedOut(), 0);
Assert.assertEquals(stats.getTotalBadDestroyed(), 0);
Assert.assertEquals(stats.getMaxPoolSize(), POOL_SIZE);
Assert.assertEquals(stats.getMinPoolSize(), 0);
Assert.assertEquals(stats.getPoolSize(), GET);
Assert.assertEquals(stats.getSampleMaxCheckedOut(), GET);
Assert.assertEquals(stats.getSampleMaxPoolSize(), GET);
// do some puts with bad objects
for (int i = 0; i < PUT_BAD; i++) {
AtomicBoolean obj = objects.remove(objects.size() - 1);
// invalidate the object
obj.set(false);
pool.put(obj);
}
clock.addDuration(SAMPLING_DURATION_INCREMENT);
stats = pool.getStats();
Assert.assertEquals(stats.getTotalCreated(), GET);
Assert.assertEquals(stats.getTotalDestroyed(), PUT_BAD);
Assert.assertEquals(stats.getTotalCreateErrors(), 0);
Assert.assertEquals(stats.getTotalDestroyErrors(), 0);
Assert.assertEquals(stats.getCheckedOut(), GET - PUT_GOOD - PUT_BAD);
Assert.assertEquals(stats.getTotalTimedOut(), 0);
Assert.assertEquals(stats.getTotalBadDestroyed(), PUT_BAD);
Assert.assertEquals(stats.getMaxPoolSize(), POOL_SIZE);
Assert.assertEquals(stats.getMinPoolSize(), 0);
Assert.assertEquals(stats.getPoolSize(), GET - PUT_BAD);
Assert.assertEquals(stats.getSampleMaxCheckedOut(), GET - PUT_GOOD);
Assert.assertEquals(stats.getSampleMaxPoolSize(), GET);
// do some disposes
for (int i = 0; i < DISPOSE; i++) {
AtomicBoolean obj = objects.remove(objects.size() - 1);
pool.dispose(obj);
}
clock.addDuration(SAMPLING_DURATION_INCREMENT);
stats = pool.getStats();
Assert.assertEquals(stats.getTotalCreated(), GET);
Assert.assertEquals(stats.getTotalDestroyed(), PUT_BAD + DISPOSE);
Assert.assertEquals(stats.getTotalCreateErrors(), 0);
Assert.assertEquals(stats.getTotalDestroyErrors(), 0);
Assert.assertEquals(stats.getCheckedOut(), GET - PUT_GOOD - PUT_BAD - DISPOSE);
Assert.assertEquals(stats.getTotalTimedOut(), 0);
Assert.assertEquals(stats.getTotalBadDestroyed(), PUT_BAD + DISPOSE);
Assert.assertEquals(stats.getMaxPoolSize(), POOL_SIZE);
Assert.assertEquals(stats.getMinPoolSize(), 0);
Assert.assertEquals(stats.getPoolSize(), GET - PUT_BAD - DISPOSE);
Assert.assertEquals(stats.getSampleMaxCheckedOut(), GET - PUT_GOOD - PUT_BAD);
Assert.assertEquals(stats.getSampleMaxPoolSize(), GET - PUT_BAD);
// wait for a reap -- should destroy the PUT_GOOD objects
Thread.sleep(DELAY);
clock.addDuration(SAMPLING_DURATION_INCREMENT);
stats = pool.getStats();
Assert.assertEquals(stats.getTotalCreated(), GET);
Assert.assertEquals(stats.getTotalDestroyed(), PUT_GOOD + PUT_BAD + DISPOSE);
Assert.assertEquals(stats.getTotalCreateErrors(), 0);
Assert.assertEquals(stats.getTotalDestroyErrors(), 0);
Assert.assertEquals(stats.getCheckedOut(), GET - PUT_GOOD - PUT_BAD - DISPOSE);
Assert.assertEquals(stats.getTotalTimedOut(), PUT_GOOD);
Assert.assertEquals(stats.getTotalBadDestroyed(), PUT_BAD + DISPOSE);
Assert.assertEquals(stats.getMaxPoolSize(), POOL_SIZE);
Assert.assertEquals(stats.getMinPoolSize(), 0);
Assert.assertEquals(stats.getPoolSize(), GET - PUT_GOOD - PUT_BAD - DISPOSE);
Assert.assertEquals(stats.getSampleMaxCheckedOut(), GET - PUT_GOOD - PUT_BAD - DISPOSE);
Assert.assertEquals(stats.getSampleMaxPoolSize(), GET - PUT_BAD - DISPOSE);
}
use of com.linkedin.r2.transport.http.client.AsyncPool in project rest.li by linkedin.
the class TestAsyncPool method testCancelTriggerShutdown.
/**
* Tests {@link AsyncPool}'s shutdown sequence is properly triggered when outstanding
* waiters cancel the previous get calls.
*/
@Test
public void testCancelTriggerShutdown() throws Exception {
SynchronousLifecycle lifecycle = new SynchronousLifecycle();
AsyncPool<Object> pool = new AsyncPoolImpl<>("object pool", lifecycle, 1, 100, _executor);
pool.start();
FutureCallback<Object> callback1 = new FutureCallback<>();
Cancellable cancellable1 = pool.get(callback1);
FutureCallback<Object> callback2 = new FutureCallback<>();
Cancellable cancellable2 = pool.get(callback2);
FutureCallback<None> shutdownCallback = new FutureCallback<>();
pool.shutdown(shutdownCallback);
// Disposes the previously checked out object. The pool now has no outstanding checkouts but waiter
// size is still one due to the second #get call above.
pool.dispose(callback1.get(5, TimeUnit.SECONDS));
// Caller cancels the second #get call. The pool should be in the right condition and initiate shutdown.
cancellable2.cancel();
// Pool should shutdown successfully without the callback timeout
shutdownCallback.get(5, TimeUnit.SECONDS);
}
use of com.linkedin.r2.transport.http.client.AsyncPool in project rest.li by linkedin.
the class TestAsyncPool method performCheckout.
private List<Object> performCheckout(int numberOfCheckouts, AsyncPool<Object> pool) {
List<Object> checkedOutObjects = new ArrayList<>(numberOfCheckouts);
ScheduledExecutorService checkoutExecutor = Executors.newScheduledThreadPool(50);
CountDownLatch checkoutLatch = new CountDownLatch(numberOfCheckouts);
Runnable checkoutTask = getCheckoutTask(pool, checkedOutObjects, new Object(), checkoutLatch, new CountDownLatch(numberOfCheckouts));
for (int i = 0; i < numberOfCheckouts; i++) {
checkoutExecutor.execute(checkoutTask);
}
try {
checkoutLatch.await(5, TimeUnit.SECONDS);
checkoutExecutor.shutdownNow();
} catch (Exception ex) {
Assert.fail("Too long to perform checkout operation");
}
return checkedOutObjects;
}
use of com.linkedin.r2.transport.http.client.AsyncPool in project rest.li by linkedin.
the class AsyncSharedPoolImpl method get.
@Override
public Cancellable get(Callback<T> callback) {
ArgumentUtil.notNull(callback, "callback");
final TimeTrackingCallback timeTrackingCallback = new TimeTrackingCallback(callback);
final LinkedDeque.Node<Callback<T>> node;
T item = null;
boolean create = false;
while (true) {
final State state;
synchronized (_lock) {
state = _state;
if (state == State.RUNNING) {
item = _item.get();
if (item == null) {
node = _waiters.size() < _maxWaiters ? _waiters.addLastNode(timeTrackingCallback) : null;
if (_isCreateInProgress) {
LOG.debug("{}: item creation is in progress", _name);
} else {
_isCreateInProgress = true;
create = true;
}
break;
}
_checkedOut++;
_statsTracker.sampleMaxCheckedOut();
}
}
if (state != State.RUNNING) {
// Defer execution of the callback until we are out of the synchronized block
timeTrackingCallback.onError(new IllegalStateException(_name + " is " + _state));
return () -> false;
}
// through the item lifecycle before passing back to user callback
if (_lifecycle.validateGet(item)) {
timeTrackingCallback.onSuccess(item);
return () -> false;
}
boolean disposed;
synchronized (_lock) {
// The connection has gone bad so we proceed to destroy it
disposed = doDispose(item);
}
if (disposed) {
doDestroy(item, BAD, () -> {
});
}
}
if (node == null) {
// This is a recoverable exception. User can simply retry the failed get() operation.
timeTrackingCallback.onError(new SizeLimitExceededException("AsyncPool " + _name + " reached maximum waiter size: " + _maxWaiters));
return () -> false;
}
// The pool is currently empty we need to construct a new item
if (create) {
doCreate();
}
return () -> {
synchronized (_lock) {
return _waiters.removeNode(node) != null;
}
};
}
Aggregations