use of com.linkedin.r2.util.Timeout 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)));
}
});
}
use of com.linkedin.r2.util.Timeout in project rest.li by linkedin.
the class SyncIOHandler method eventLoop.
private void eventLoop() throws ServletException, IOException, InterruptedException, TimeoutException {
final long startTime = System.currentTimeMillis();
byte[] buf = new byte[DEFAULT_DATA_CHUNK_SIZE];
while (shouldContinue() && !_forceExit) {
long timeSpent = System.currentTimeMillis() - startTime;
long maxWaitTime = timeSpent < _timeout ? _timeout - timeSpent : 0;
Event event = _eventQueue.poll(maxWaitTime, TimeUnit.MILLISECONDS);
if (event == null) {
throw new TimeoutException("Timeout after " + _timeout + " milliseconds.");
}
switch(event.getEventType()) {
case ResponseDataAvailable:
{
ByteString data = (ByteString) event.getData();
data.write(_os);
_rh.request(1);
break;
}
case WriteRequestPossible:
{
while (_wh.remaining() > 0) {
final int actualLen = _is.read(buf);
if (actualLen < 0) {
_wh.done();
_requestReadFinished = true;
break;
}
_wh.write(ByteString.copy(buf, 0, actualLen));
}
break;
}
case FullResponseReceived:
{
_os.close();
_responseWriteFinished = true;
break;
}
case ResponseDataError:
{
_os.close();
_responseWriteFinished = true;
break;
}
case WriteRequestAborted:
{
if (event.getData() instanceof AbortedException) {
// reader cancels, we'll drain the stream on behalf of reader
// we don't directly drain it here because we'd like to give other events
// some opportunities to be executed; e.g. return an error response
_eventQueue.add(Event.DrainRequestEvent);
} else {
// TODO: do we want to be smarter and return server error response?
throw new ServletException((Throwable) event.getData());
}
break;
}
case DrainRequest:
{
for (int i = 0; i < 10; i++) {
final int actualLen = _is.read(buf);
if (actualLen < 0) {
_requestReadFinished = true;
break;
}
}
if (!_requestReadFinished) {
// add self back to event queue and give others a chance to run
_eventQueue.add(Event.DrainRequestEvent);
}
break;
}
case ForceExit:
{
_forceExit = true;
break;
}
default:
throw new IllegalStateException("Unknown event type:" + event.getEventType());
}
}
}
use of com.linkedin.r2.util.Timeout in project rest.li by linkedin.
the class TestDisruptor method testStreamErrorDisrupt.
@Test
public void testStreamErrorDisrupt() throws Exception {
final Map<String, Object> properties = new HashMap<>();
properties.put(HttpClientFactory.HTTP_REQUEST_TIMEOUT, String.valueOf(REQUEST_TIMEOUT));
final Client client = _clientProvider.createClient(FilterChains.empty(), properties);
final RequestContext requestContext = new RequestContext();
requestContext.putLocalAttr(DISRUPT_CONTEXT_KEY, DisruptContexts.error(REQUEST_LATENCY));
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean success = new AtomicBoolean(false);
client.streamRequest(new StreamRequestBuilder(getHttpURI()).build(EntityStreams.emptyStream()), requestContext, new Callback<StreamResponse>() {
@Override
public void onSuccess(StreamResponse result) {
latch.countDown();
}
@Override
public void onError(Throwable e) {
success.set(e instanceof DisruptedException);
latch.countDown();
}
});
Assert.assertTrue(latch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS), "Test execution timeout");
Assert.assertTrue(success.get(), "Unexpected transport response");
}
use of com.linkedin.r2.util.Timeout in project rest.li by linkedin.
the class TestDisruptor method testStreamLatencyDisrupt.
@Test
public void testStreamLatencyDisrupt() throws Exception {
final RequestContext requestContext = new RequestContext();
requestContext.putLocalAttr(DISRUPT_CONTEXT_KEY, DisruptContexts.delay(REQUEST_LATENCY));
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean success = new AtomicBoolean(false);
_client.streamRequest(new StreamRequestBuilder(getHttpURI()).build(EntityStreams.emptyStream()), requestContext, new Callback<StreamResponse>() {
@Override
public void onSuccess(StreamResponse result) {
success.set(true);
latch.countDown();
}
@Override
public void onError(Throwable e) {
success.set(false);
latch.countDown();
}
});
Assert.assertTrue(latch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS), "Test execution timeout");
Assert.assertTrue(success.get(), "Unexpected transport response");
}
use of com.linkedin.r2.util.Timeout in project rest.li by linkedin.
the class TestDisruptor method testRestErrorDisrupt.
@Test
public void testRestErrorDisrupt() throws Exception {
final Map<String, Object> properties = new HashMap<>();
properties.put(HttpClientFactory.HTTP_REQUEST_TIMEOUT, String.valueOf(REQUEST_TIMEOUT));
final Client client = _clientProvider.createClient(FilterChains.empty(), properties);
final RequestContext requestContext = new RequestContext();
requestContext.putLocalAttr(DISRUPT_CONTEXT_KEY, DisruptContexts.error(REQUEST_LATENCY));
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean success = new AtomicBoolean(false);
client.restRequest(new RestRequestBuilder(getHttpURI()).build(), requestContext, new Callback<RestResponse>() {
@Override
public void onSuccess(RestResponse result) {
latch.countDown();
}
@Override
public void onError(Throwable e) {
success.set(e instanceof DisruptedException);
latch.countDown();
}
});
Assert.assertTrue(latch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS), "Test execution timeout");
Assert.assertTrue(success.get(), "Unexpected transport response");
}
Aggregations