Search in sources :

Example 1 with ObjectCreationTimeoutException

use of com.linkedin.r2.transport.http.client.ObjectCreationTimeoutException 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);
}
Also used : LongTracking(com.linkedin.common.stats.LongTracking) ArrayList(java.util.ArrayList) ClockedExecutor(com.linkedin.test.util.ClockedExecutor) 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) AsyncPoolImpl(com.linkedin.r2.transport.http.client.AsyncPoolImpl) ExponentialBackOffRateLimiter(com.linkedin.r2.transport.http.client.ExponentialBackOffRateLimiter) FutureCallback(com.linkedin.common.callback.FutureCallback) ObjectCreationTimeoutException(com.linkedin.r2.transport.http.client.ObjectCreationTimeoutException) Test(org.testng.annotations.Test)

Example 2 with ObjectCreationTimeoutException

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

the class AsyncPoolImpl method create.

/**
 * DO NOT call this method while holding the lock!  It invokes user code.
 */
private void create() {
    trc("initiating object creation");
    _rateLimiter.submit(new Task() {

        @Override
        public void run(final SimpleCallback callback) {
            boolean shouldIgnore;
            synchronized (_lock) {
                // Ignore the object creation if no one is waiting for the object and the pool already has _minSize objects
                int totalObjects = _checkedOut + _idle.size();
                shouldIgnore = _waiters.size() == 0 && totalObjects >= _minSize;
                if (shouldIgnore) {
                    _statsTracker.incrementIgnoredCreation();
                    if (_poolSize >= 1) {
                        // _poolSize also include the count of creation requests pending. So we have to make sure the pool size
                        // count is updated when we ignore the creation request.
                        _poolSize--;
                    }
                }
            }
            if (shouldIgnore) {
                callback.onDone();
                return;
            }
            // Lets not trust the _lifecycle to timely return a response here.
            // Embedding the callback inside a timeout callback (ObjectCreationTimeoutCallback)
            // to force a response within creationTimeout deadline to reclaim the object slot in the pool
            _lifecycle.create(new TimeoutCallback<>(_timeoutExecutor, _creationTimeout, TimeUnit.MILLISECONDS, new Callback<T>() {

                @Override
                public void onSuccess(T t) {
                    synchronized (_lock) {
                        _statsTracker.incrementCreated();
                        _lastCreateError = null;
                    }
                    add(t);
                    callback.onDone();
                }

                @Override
                public void onError(final Throwable e) {
                    // Note we drain all waiters and cancel all pending creates if a create fails.
                    // When a create fails, rate-limiting logic will be applied.  In this case,
                    // we may be initiating creations at a lower rate than incoming requests.  While
                    // creations are suppressed, it is better to deny all waiters and let them see
                    // the real reason (this exception) rather than keep them around to eventually
                    // get an unhelpful timeout error
                    final Collection<Callback<T>> waitersDenied;
                    final Collection<Task> cancelledCreate = _rateLimiter.cancelPendingTasks();
                    boolean create;
                    synchronized (_lock) {
                        _statsTracker.incrementCreateErrors();
                        _lastCreateError = e;
                        // Cancel all waiters in the rate limiter
                        if (!_waiters.isEmpty()) {
                            waitersDenied = cancelWaiters();
                        } else {
                            waitersDenied = Collections.<Callback<T>>emptyList();
                        }
                        // reclaim the slot in the pool
                        create = objectDestroyed(1 + cancelledCreate.size());
                    }
                    // lets fail all the waiters with the object creation error
                    for (Callback<T> denied : waitersDenied) {
                        try {
                            denied.onError(e);
                        } catch (Exception ex) {
                            LOG.error("Encountered error while invoking error waiter callback", ex);
                        }
                    }
                    // Now after cancelling all the pending tasks, lets make sure to back off on the creation
                    _rateLimiter.incrementPeriod();
                    // the min poolSize
                    if (create) {
                        create();
                    }
                    LOG.debug(_poolName + ": object creation failed", e);
                    callback.onDone();
                }
            }, () -> new ObjectCreationTimeoutException("Exceeded creation timeout of " + _creationTimeout + "ms: in Pool: " + _poolName)));
        }
    });
}
Also used : Task(com.linkedin.r2.transport.http.client.RateLimiter.Task) Callback(com.linkedin.common.callback.Callback) SimpleCallback(com.linkedin.common.callback.SimpleCallback) Collection(java.util.Collection) SimpleCallback(com.linkedin.common.callback.SimpleCallback) SizeLimitExceededException(com.linkedin.r2.SizeLimitExceededException)

Aggregations

Callback (com.linkedin.common.callback.Callback)1 FutureCallback (com.linkedin.common.callback.FutureCallback)1 SimpleCallback (com.linkedin.common.callback.SimpleCallback)1 LongTracking (com.linkedin.common.stats.LongTracking)1 SizeLimitExceededException (com.linkedin.r2.SizeLimitExceededException)1 AsyncPoolImpl (com.linkedin.r2.transport.http.client.AsyncPoolImpl)1 ExponentialBackOffRateLimiter (com.linkedin.r2.transport.http.client.ExponentialBackOffRateLimiter)1 ObjectCreationTimeoutException (com.linkedin.r2.transport.http.client.ObjectCreationTimeoutException)1 PoolStats (com.linkedin.r2.transport.http.client.PoolStats)1 Task (com.linkedin.r2.transport.http.client.RateLimiter.Task)1 ClockedExecutor (com.linkedin.test.util.ClockedExecutor)1 ArrayList (java.util.ArrayList)1 Collection (java.util.Collection)1 ExecutionException (java.util.concurrent.ExecutionException)1 TimeoutException (java.util.concurrent.TimeoutException)1 Test (org.testng.annotations.Test)1