use of com.linkedin.r2.transport.http.client.RateLimiter.Task in project rest.li by linkedin.
the class TestRestLiMethodInvocation method testStreaming.
@Test
public void testStreaming() throws RestLiSyntaxException, URISyntaxException {
Map<String, ResourceModel> resourceModelMap = buildResourceModels(StatusCollectionResource.class, AsyncStatusCollectionResource.class, PromiseStatusCollectionResource.class, TaskStatusCollectionResource.class);
final String payload = "{\"metadata\": \"someMetadata\"}";
ResourceModel statusResourceModel = resourceModelMap.get("/statuses");
ResourceModel asyncStatusResourceModel = resourceModelMap.get("/asyncstatuses");
ResourceModel promiseStatusResourceModel = resourceModelMap.get("/promisestatuses");
ResourceModel taskStatusResourceModel = resourceModelMap.get("/taskstatuses");
ResourceMethodDescriptor methodDescriptor;
StatusCollectionResource statusResource;
AsyncStatusCollectionResource asyncStatusResource;
PromiseStatusCollectionResource promiseStatusResource;
TaskStatusCollectionResource taskStatusResource;
//Sync Method Execution - Successful scenario
methodDescriptor = statusResourceModel.findActionMethod("streamingAction", ResourceLevel.COLLECTION);
statusResource = getMockResource(StatusCollectionResource.class);
EasyMock.expect(statusResource.streamingAction(EasyMock.<String>anyObject(), EasyMock.<RestLiAttachmentReader>anyObject())).andReturn(1234l).once();
checkInvocation(statusResource, methodDescriptor, "POST", version, "/statuses/?action=streamingAction", payload, null, new RequestExecutionCallback<RestResponse>() {
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request failed unexpectedly.");
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
}
}, false, false, new RestLiAttachmentReader(null), new RestLiResponseAttachments.Builder().build());
//Sync Method Execution - Error scenario
statusResource = getMockResource(StatusCollectionResource.class);
EasyMock.expect(statusResource.streamingAction(EasyMock.<String>anyObject(), EasyMock.<RestLiAttachmentReader>anyObject())).andThrow(new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR)).once();
checkInvocation(statusResource, methodDescriptor, "POST", version, "/statuses/?action=streamingAction", payload, null, new RequestExecutionCallback<RestResponse>() {
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request passed unexpectedly.");
}
}, false, false, new RestLiAttachmentReader(null), new RestLiResponseAttachments.Builder().build());
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Callback Method Execution - Successful scenario
methodDescriptor = asyncStatusResourceModel.findMethod(ResourceMethod.ACTION);
asyncStatusResource = getMockResource(AsyncStatusCollectionResource.class);
asyncStatusResource.streamingAction(EasyMock.<String>anyObject(), EasyMock.<RestLiAttachmentReader>anyObject(), EasyMock.<Callback<Long>>anyObject());
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
@SuppressWarnings("unchecked") Callback<Long> callback = (Callback<Long>) EasyMock.getCurrentArguments()[2];
callback.onSuccess(1234l);
return null;
}
});
checkInvocation(asyncStatusResource, methodDescriptor, "POST", version, "/asyncstatuses/?action=streamingAction", payload, null, new RequestExecutionCallback<RestResponse>() {
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request failed unexpectedly.");
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
}
}, false, false, new RestLiAttachmentReader(null), new RestLiResponseAttachments.Builder().build());
//Callback Method Execution - Error scenario
asyncStatusResource = getMockResource(AsyncStatusCollectionResource.class);
asyncStatusResource.streamingAction(EasyMock.<String>anyObject(), EasyMock.<RestLiAttachmentReader>anyObject(), EasyMock.<Callback<Long>>anyObject());
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
@SuppressWarnings("unchecked") Callback<Long> callback = (Callback<Long>) EasyMock.getCurrentArguments()[2];
callback.onError(new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR));
return null;
}
});
checkInvocation(asyncStatusResource, methodDescriptor, "POST", version, "/asyncstatuses/?action=streamingAction", payload, null, new RequestExecutionCallback<RestResponse>() {
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request passed unexpectedly.");
}
}, false, false, new RestLiAttachmentReader(null), new RestLiResponseAttachments.Builder().build());
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Promise Method Execution - Successful scenario
methodDescriptor = promiseStatusResourceModel.findActionMethod("streamingAction", ResourceLevel.COLLECTION);
promiseStatusResource = getMockResource(PromiseStatusCollectionResource.class);
promiseStatusResource.streamingAction(EasyMock.<String>anyObject(), EasyMock.<RestLiAttachmentReader>anyObject());
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
final SettablePromise<Long> result = Promises.settable();
final Runnable requestHandler = new Runnable() {
public void run() {
try {
result.done(1234l);
} catch (final Throwable throwable) {
result.fail(throwable);
}
}
};
_scheduler.schedule(requestHandler, 0, TimeUnit.MILLISECONDS);
return result;
}
});
checkInvocation(promiseStatusResource, methodDescriptor, "POST", version, "/promisestatuses/?action=streamingAction", payload, null, new RequestExecutionCallback<RestResponse>() {
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request failed unexpectedly.");
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
}
}, false, false, new RestLiAttachmentReader(null), new RestLiResponseAttachments.Builder().build());
//Promise Method Execution - Error scenario
promiseStatusResource = getMockResource(PromiseStatusCollectionResource.class);
promiseStatusResource.streamingAction(EasyMock.<String>anyObject(), EasyMock.<RestLiAttachmentReader>anyObject());
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
final SettablePromise<Long> result = Promises.settable();
final Runnable requestHandler = new Runnable() {
public void run() {
result.fail(new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR));
}
};
_scheduler.schedule(requestHandler, 0, TimeUnit.MILLISECONDS);
return result;
}
});
checkInvocation(promiseStatusResource, methodDescriptor, "POST", version, "/promisestatuses/?action=streamingAction", payload, null, new RequestExecutionCallback<RestResponse>() {
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request passed unexpectedly.");
}
}, false, false, new RestLiAttachmentReader(null), new RestLiResponseAttachments.Builder().build());
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Task Method Execution - Successful scenario
methodDescriptor = taskStatusResourceModel.findMethod(ResourceMethod.ACTION);
taskStatusResource = getMockResource(TaskStatusCollectionResource.class);
taskStatusResource.streamingAction(EasyMock.<String>anyObject(), EasyMock.<RestLiAttachmentReader>anyObject());
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
return new BaseTask<Long>() {
protected Promise<Long> run(final Context context) throws Exception {
return Promises.value(1234l);
}
};
}
});
checkInvocation(taskStatusResource, methodDescriptor, "POST", version, "/taskstatuses/?action=streamingAction", payload, null, new RequestExecutionCallback<RestResponse>() {
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request failed unexpectedly.");
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
}
}, false, false, new RestLiAttachmentReader(null), new RestLiResponseAttachments.Builder().build());
//Task Method Execution - Error scenario
taskStatusResource = getMockResource(TaskStatusCollectionResource.class);
taskStatusResource.streamingAction(EasyMock.<String>anyObject(), EasyMock.<RestLiAttachmentReader>anyObject());
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
return new BaseTask<Long>() {
protected Promise<Long> run(final Context context) throws Exception {
return Promises.error(new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR));
}
};
}
});
checkInvocation(taskStatusResource, methodDescriptor, "POST", version, "/taskstatuses/?action=streamingAction", payload, null, new RequestExecutionCallback<RestResponse>() {
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request passed unexpectedly.");
}
}, false, false, new RestLiAttachmentReader(null), new RestLiResponseAttachments.Builder().build());
}
use of com.linkedin.r2.transport.http.client.RateLimiter.Task in project rest.li by linkedin.
the class TestRateLimiter method testSimple.
@Test
public void testSimple() throws Exception {
final int total = 10;
// NB on Solaris x86 there seems to be an extra 10ms that gets added to the period; need
// to figure this out. For now set the period high enough that period + 10 will be within
// the tolerance.
final int period = 100;
final CountDownLatch latch = new CountDownLatch(total);
final Task incr = new Task() {
@Override
public void run(SimpleCallback doneCallback) {
latch.countDown();
doneCallback.onDone();
}
};
RateLimiter limiter = new ExponentialBackOffRateLimiter(period, period, period, _executor);
limiter.setPeriod(period);
long start = System.currentTimeMillis();
long lowTolerance = (total * period) * 4 / 5;
long highTolerance = (total * period) * 5 / 4;
for (int i = 0; i < total * period; i++) {
limiter.submit(incr);
}
Assert.assertTrue(latch.await(highTolerance, TimeUnit.MILLISECONDS), "Should have finished within " + highTolerance + "ms");
long t = System.currentTimeMillis() - start;
Assert.assertTrue(t > lowTolerance, "Should have finished after " + lowTolerance + "ms (took " + t + ")");
}
use of com.linkedin.r2.transport.http.client.RateLimiter.Task in project rest.li by linkedin.
the class TestRestLiMethodInvocation method testExecutionReport.
@Test
public void testExecutionReport() throws RestLiSyntaxException, URISyntaxException {
Map<String, ResourceModel> resourceModelMap = buildResourceModels(StatusCollectionResource.class, AsyncStatusCollectionResource.class, PromiseStatusCollectionResource.class, TaskStatusCollectionResource.class);
ResourceModel statusResourceModel = resourceModelMap.get("/statuses");
ResourceModel asyncStatusResourceModel = resourceModelMap.get("/asyncstatuses");
ResourceModel promiseStatusResourceModel = resourceModelMap.get("/promisestatuses");
ResourceModel taskStatusResourceModel = resourceModelMap.get("/taskstatuses");
ResourceMethodDescriptor methodDescriptor;
StatusCollectionResource statusResource;
AsyncStatusCollectionResource asyncStatusResource;
PromiseStatusCollectionResource promiseStatusResource;
TaskStatusCollectionResource taskStatusResource;
// #1: Sync Method Execution
methodDescriptor = statusResourceModel.findMethod(ResourceMethod.GET);
statusResource = getMockResource(StatusCollectionResource.class);
EasyMock.expect(statusResource.get(eq(1L))).andReturn(null).once();
checkInvocation(statusResource, methodDescriptor, "GET", version, "/statuses/1", null, buildPathKeys("statusID", 1L), new RequestExecutionCallback<RestResponse>() {
//A 404 is considered an error by rest.li
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
Assert.assertNull(executionReport.getParseqTrace(), "There should be no parseq trace!");
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request failed unexpectedly.");
}
}, true, false);
// #2: Callback based Async Method Execution
Capture<RequestExecutionReport> requestExecutionReportCapture = new Capture<RequestExecutionReport>();
RestLiCallback<?> callback = getCallback(requestExecutionReportCapture);
methodDescriptor = asyncStatusResourceModel.findMethod(ResourceMethod.GET);
asyncStatusResource = getMockResource(AsyncStatusCollectionResource.class);
asyncStatusResource.get(eq(1L), EasyMock.<Callback<Status>>anyObject());
EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
@SuppressWarnings("unchecked") Callback<Status> callback = (Callback<Status>) EasyMock.getCurrentArguments()[1];
callback.onSuccess(null);
return null;
}
});
EasyMock.replay(asyncStatusResource);
checkAsyncInvocation(asyncStatusResource, callback, methodDescriptor, "GET", version, "/asyncstatuses/1", null, buildPathKeys("statusID", 1L), true);
Assert.assertNull(requestExecutionReportCapture.getValue().getParseqTrace());
// #3: Promise based Async Method Execution
methodDescriptor = promiseStatusResourceModel.findMethod(ResourceMethod.GET);
promiseStatusResource = getMockResource(PromiseStatusCollectionResource.class);
EasyMock.expect(promiseStatusResource.get(eq(1L))).andReturn(Promises.<Status>value(null)).once();
checkInvocation(promiseStatusResource, methodDescriptor, "GET", version, "/promisestatuses/1", null, buildPathKeys("statusID", 1L), new RequestExecutionCallback<RestResponse>() {
//A 404 is considered an error by rest.li
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
Assert.assertNotNull(executionReport.getParseqTrace(), "There should be a valid parseq trace!");
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request failed unexpectedly.");
}
}, true, false);
// #4: Task based Async Method Execution
methodDescriptor = taskStatusResourceModel.findMethod(ResourceMethod.GET);
taskStatusResource = getMockResource(TaskStatusCollectionResource.class);
EasyMock.expect(taskStatusResource.get(eq(1L))).andReturn(Task.callable("myTask", new Callable<Status>() {
@Override
public Status call() throws Exception {
return new Status();
}
})).once();
checkInvocation(taskStatusResource, methodDescriptor, "GET", version, "/taskstatuses/1", null, buildPathKeys("statusID", 1L), new RequestExecutionCallback<RestResponse>() {
@Override
public void onError(Throwable e, RequestExecutionReport executionReport, RestLiAttachmentReader requestAttachmentReader, RestLiResponseAttachments responseAttachments) {
Assert.fail("Request failed unexpectedly.");
}
@Override
public void onSuccess(RestResponse result, RequestExecutionReport executionReport, RestLiResponseAttachments responseAttachments) {
Assert.assertNotNull(executionReport.getParseqTrace());
}
}, true, false);
}
use of com.linkedin.r2.transport.http.client.RateLimiter.Task 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) {
_lifecycle.create(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) {
_rateLimiter.incrementPeriod();
// 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;
create = objectDestroyed(1 + cancelledCreate.size());
if (!_waiters.isEmpty()) {
waitersDenied = cancelWaiters();
} else {
waitersDenied = Collections.<Callback<T>>emptyList();
}
}
for (Callback<T> denied : waitersDenied) {
try {
denied.onError(e);
} catch (Exception ex) {
LOG.error("Encountered error while invoking error waiter callback", ex);
}
}
if (create) {
create();
}
LOG.error(_poolName + ": object creation failed", e);
callback.onDone();
}
});
}
});
}
use of com.linkedin.r2.transport.http.client.RateLimiter.Task in project rest.li by linkedin.
the class HttpNettyClient method writeRequest.
private void writeRequest(RestRequest request, RequestContext requestContext, Map<String, String> wireAttrs, final TimeoutTransportCallback<RestResponse> callback) {
State state = _state.get();
if (state != State.RUNNING) {
errorResponse(callback, new IllegalStateException("Client is " + state));
return;
}
URI uri = request.getURI();
String scheme = uri.getScheme();
if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) {
errorResponse(callback, new IllegalArgumentException("Unknown scheme: " + scheme + " (only http/https is supported)"));
return;
}
String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
port = "http".equalsIgnoreCase(scheme) ? HTTP_DEFAULT_PORT : HTTPS_DEFAULT_PORT;
}
final RestRequest newRequest = new RestRequestBuilder(request).overwriteHeaders(WireAttributeHelper.toWireAttributes(wireAttrs)).build();
final SocketAddress address;
try {
// TODO investigate DNS resolution and timing
InetAddress inetAddress = InetAddress.getByName(host);
address = new InetSocketAddress(inetAddress, port);
requestContext.putLocalAttr(R2Constants.REMOTE_SERVER_ADDR, inetAddress.getHostAddress());
} catch (UnknownHostException e) {
errorResponse(callback, e);
return;
}
requestContext.putLocalAttr(R2Constants.HTTP_PROTOCOL_VERSION, HttpProtocolVersion.HTTP_1_1);
final AsyncPool<Channel> pool;
try {
pool = _channelPoolManager.getPoolForAddress(address);
} catch (IllegalStateException e) {
errorResponse(callback, e);
return;
}
final Cancellable pendingGet = pool.get(new Callback<Channel>() {
@Override
public void onSuccess(final Channel channel) {
// This handler ensures the channel is returned to the pool at the end of the
// Netty pipeline.
channel.attr(ChannelPoolHandler.CHANNEL_POOL_ATTR_KEY).set(pool);
callback.addTimeoutTask(new Runnable() {
@Override
public void run() {
AsyncPool<Channel> pool = channel.attr(ChannelPoolHandler.CHANNEL_POOL_ATTR_KEY).getAndRemove();
if (pool != null) {
pool.dispose(channel);
}
}
});
// This handler invokes the callback with the response once it arrives.
channel.attr(RAPResponseHandler.CALLBACK_ATTR_KEY).set(callback);
final State state = _state.get();
if (state == State.REQUESTS_STOPPING || state == State.SHUTDOWN) {
// In this case, we acquired a channel from the pool as request processing is halting.
// The shutdown task might not timeout this callback, since it may already have scanned
// all the channels for pending requests before we set the callback as the channel
// attachment. The TimeoutTransportCallback ensures the user callback in never
// invoked more than once, so it is safe to invoke it unconditionally.
errorResponse(callback, new TimeoutException("Operation did not complete before shutdown"));
return;
}
// here we want the exception in outbound operations to be passed back through pipeline so that
// the user callback would be invoked with the exception and the channel can be put back into the pool
channel.writeAndFlush(newRequest).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
@Override
public void onError(Throwable e) {
errorResponse(callback, e);
}
});
if (pendingGet != null) {
callback.addTimeoutTask(new Runnable() {
@Override
public void run() {
pendingGet.cancel();
}
});
}
}
Aggregations