use of com.linkedin.common.callback.Callback in project rest.li by linkedin.
the class DegraderLoadBalancerStrategyV3 method checkQuarantineState.
/**
* checkQuarantineState decides if the D2Quarantine can be enabled or not, by health
* checking all the trackerClients once. It enables quarantine only if at least one of the
* clients return success for the checking.
*
* The reasons for this checking include:
*
* . The default method "OPTIONS" is not always enabled by the service
* . The user can config any path/method for checking. We do a sanity checking to
* make sure the configuration is correct, and service/host responds in time.
* Otherwise the host can be kept in quarantine forever if we blindly enable it.
*
* This check actually can warm up the R2 connection pool by making a connection to
* each trackerClient. However since the check happens before any real requests are sent,
* it generally takes much longer time to get the results, due to different warming up
* requirements. Therefore the checking will be retried in next update if current check
* fails.
*
* This function is supposed to be protected by the update lock.
*
* @param clients
* @param config
*/
private void checkQuarantineState(List<TrackerClientUpdater> clients, DegraderLoadBalancerStrategyConfig config) {
Callback<None> healthCheckCallback = new Callback<None>() {
@Override
public void onError(Throwable e) {
// Do nothing as the quarantine is disabled by default
_rateLimitedLogger.error("Error to enable quarantine. Health checking failed for service {}: ", _state._serviceName, e);
}
@Override
public void onSuccess(None result) {
if (_state._enableQuarantine.compareAndSet(false, true)) {
_log.info("Quarantine is enabled for service {}", _state._serviceName);
}
}
};
// Ideally we would like to healthchecking all the service hosts (ie all TrackerClients) because
// this can help to warm up the R2 connections to the service hosts, thus speed up the initial access
// speed when d2client starts to access those hosts. However this can expose/expedite the problem that
// the d2client host needs too many connections or file handles to all the hosts, when the downstream
// services have large amount of hosts. Before that problem is addressed, we limit the number of hosts
// for pre-healthchecking to a small number
clients.stream().limit(MAX_HOSTS_TO_CHECK_QUARANTINE).forEach(client -> {
try {
HealthCheck healthCheckClient = _state.getHealthCheckClient(client);
if (healthCheckClient == null) {
healthCheckClient = new HealthCheckClientBuilder().setHealthCheckOperations(config.getHealthCheckOperations()).setHealthCheckPath(config.getHealthCheckPath()).setServicePath(config.getServicePath()).setClock(config.getClock()).setLatency(config.getQuarantineLatency()).setMethod(config.getHealthCheckMethod()).setClient(client.getTrackerClient()).build();
_state.putHealthCheckClient(client, healthCheckClient);
}
healthCheckClient.checkHealth(healthCheckCallback);
} catch (URISyntaxException e) {
_log.error("Error to build healthCheckClient ", e);
}
});
// also remove the entries that the corresponding trackerClientUpdaters do not exist anymore
for (TrackerClientUpdater client : _state._healthCheckMap.keySet()) {
if (!clients.contains(client)) {
_state._healthCheckMap.remove(client);
}
}
}
use of com.linkedin.common.callback.Callback in project rest.li by linkedin.
the class TrackerClientTest method testCallTrackingRestRequest.
@Test
public void testCallTrackingRestRequest() throws Exception {
URI uri = URI.create("http://test.qa.com:1234/foo");
SettableClock clock = new SettableClock();
AtomicInteger action = new AtomicInteger(0);
TransportClient tc = new TransportClient() {
@Override
public void restRequest(RestRequest request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<RestResponse> callback) {
clock.addDuration(5);
switch(action.get()) {
// success
case 0:
callback.onResponse(TransportResponseImpl.success(RestResponse.NO_RESPONSE));
break;
// fail with rest exception
case 1:
callback.onResponse(TransportResponseImpl.error(RestException.forError(500, "rest exception")));
break;
// fail with timeout exception
case 2:
callback.onResponse(TransportResponseImpl.error(new RemoteInvocationException(new TimeoutException())));
break;
// fail with other exception
default:
callback.onResponse(TransportResponseImpl.error(new RuntimeException()));
break;
}
}
@Override
public void shutdown(Callback<None> callback) {
}
};
TrackerClient client = createTrackerClient(tc, clock, uri);
CallTracker callTracker = client.getCallTracker();
CallTracker.CallStats stats;
DegraderControl degraderControl = client.getDegraderControl(DefaultPartitionAccessor.DEFAULT_PARTITION_ID);
client.restRequest(new RestRequestBuilder(uri).build(), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 0);
Assert.assertEquals(stats.getCallCountTotal(), 1);
Assert.assertEquals(stats.getErrorCountTotal(), 0);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.0, 0.001);
action.set(1);
client.restRequest(new RestRequestBuilder(uri).build(), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 2);
Assert.assertEquals(stats.getErrorCountTotal(), 1);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.2, 0.001);
action.set(2);
client.restRequest(new RestRequestBuilder(uri).build(), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 3);
Assert.assertEquals(stats.getErrorCountTotal(), 2);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.4, 0.001);
action.set(3);
client.restRequest(new RestRequestBuilder(uri).build(), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 4);
Assert.assertEquals(stats.getErrorCountTotal(), 3);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.2, 0.001);
}
use of com.linkedin.common.callback.Callback in project rest.li by linkedin.
the class TrackerClientTest method testCallTrackingStreamRequest.
@Test
public void testCallTrackingStreamRequest() throws Exception {
URI uri = URI.create("http://test.qa.com:1234/foo");
SettableClock clock = new SettableClock();
AtomicInteger action = new AtomicInteger(0);
TransportClient tc = new TransportClient() {
@Override
public void restRequest(RestRequest request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<RestResponse> callback) {
}
@Override
public void streamRequest(StreamRequest request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<StreamResponse> callback) {
clock.addDuration(5);
switch(action.get()) {
// success
case 0:
callback.onResponse(TransportResponseImpl.success(new StreamResponseBuilder().build(EntityStreams.emptyStream())));
break;
// fail with stream exception
case 1:
callback.onResponse(TransportResponseImpl.error(new StreamException(new StreamResponseBuilder().setStatus(500).build(EntityStreams.emptyStream()))));
break;
// fail with timeout exception
case 2:
callback.onResponse(TransportResponseImpl.error(new RemoteInvocationException(new TimeoutException())));
break;
// fail with other exception
default:
callback.onResponse(TransportResponseImpl.error(new RuntimeException()));
break;
}
}
@Override
public void shutdown(Callback<None> callback) {
}
};
TrackerClient client = createTrackerClient(tc, clock, uri);
CallTracker callTracker = client.getCallTracker();
CallTracker.CallStats stats;
DegraderControl degraderControl = client.getDegraderControl(DefaultPartitionAccessor.DEFAULT_PARTITION_ID);
DelayConsumeCallback delayConsumeCallback = new DelayConsumeCallback();
client.streamRequest(new StreamRequestBuilder(uri).build(EntityStreams.emptyStream()), new RequestContext(), new HashMap<>(), delayConsumeCallback);
clock.addDuration(5);
// we only recorded the time when stream response arrives, but callcompletion.endcall hasn't been called yet.
Assert.assertEquals(callTracker.getCurrentCallCountTotal(), 0);
Assert.assertEquals(callTracker.getCurrentErrorCountTotal(), 0);
// delay
clock.addDuration(100);
delayConsumeCallback.consume();
clock.addDuration(5000);
// now that we consumed the entity stream, callcompletion.endcall has been called.
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 0);
Assert.assertEquals(stats.getCallCountTotal(), 1);
Assert.assertEquals(stats.getErrorCountTotal(), 0);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.0, 0.001);
action.set(1);
client.streamRequest(new StreamRequestBuilder(uri).build(EntityStreams.emptyStream()), new RequestContext(), new HashMap<>(), delayConsumeCallback);
clock.addDuration(5);
// we endcall with error immediately for stream exception, even before the entity is consumed
Assert.assertEquals(callTracker.getCurrentCallCountTotal(), 2);
Assert.assertEquals(callTracker.getCurrentErrorCountTotal(), 1);
delayConsumeCallback.consume();
clock.addDuration(5000);
// no change in tracking after entity is consumed
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 2);
Assert.assertEquals(stats.getErrorCountTotal(), 1);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.2, 0.001);
action.set(2);
client.streamRequest(new StreamRequestBuilder(uri).build(EntityStreams.emptyStream()), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5);
Assert.assertEquals(callTracker.getCurrentCallCountTotal(), 3);
Assert.assertEquals(callTracker.getCurrentErrorCountTotal(), 2);
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 3);
Assert.assertEquals(stats.getErrorCountTotal(), 2);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.4, 0.001);
action.set(3);
client.streamRequest(new StreamRequestBuilder(uri).build(EntityStreams.emptyStream()), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5);
Assert.assertEquals(callTracker.getCurrentCallCountTotal(), 4);
Assert.assertEquals(callTracker.getCurrentErrorCountTotal(), 3);
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 4);
Assert.assertEquals(stats.getErrorCountTotal(), 3);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.2, 0.001);
}
use of com.linkedin.common.callback.Callback in project rest.li by linkedin.
the class ZKFSLoadBalancer method start.
@Override
public void start(final Callback<None> callback) {
LOG.info("Starting ZKFSLoadBalancer");
LOG.info("ZK connect string: {}", _connectString);
LOG.info("ZK session timeout: {}ms", _sessionTimeout);
LOG.info("ZK initial connect timeout: {}ms", _initialZKTimeout);
if (_connectString == null || _connectString.isEmpty()) {
callback.onError(new IllegalArgumentException("ZooKeeper connection string is null or empty"));
return;
}
if (_zkFlagFile == null) {
LOG.info("ZK flag file not specified");
} else {
LOG.info("ZK flag file: {}", _zkFlagFile.getAbsolutePath());
LOG.info("ZK currently suppressed by flag file: {}", suppressZK());
}
_zkConnection = new ZKConnection(_connectString, _sessionTimeout, _shutdownAsynchronously, _isSymlinkAware);
final TogglingLoadBalancer balancer = _loadBalancerFactory.createLoadBalancer(_zkConnection, _executor);
// all other cases, we service requests from the old LoadBalancer until the new one is started
if (_currentLoadBalancer == null) {
_currentLoadBalancer = balancer;
}
Callback<None> wrapped = new Callback<None>() {
@Override
public void onSuccess(None none) {
_currentLoadBalancer = balancer;
callback.onSuccess(none);
}
@Override
public void onError(Throwable e) {
callback.onError(e);
}
};
if (!_startupCallback.compareAndSet(null, wrapped)) {
throw new IllegalStateException("Startup already in progress");
}
_executor.execute(new PropertyEventThread.PropertyEvent("startup") {
@Override
public void innerRun() {
_zkConnection.addStateListener(new ZKListener(balancer));
try {
_zkConnection.start();
} catch (Exception e) {
LOG.error("Failed to start ZooKeeper (bad configuration?), enabling backup stores", e);
Callback<None> startupCallback = _startupCallback.getAndSet(null);
// TODO this should never be null
balancer.enableBackup(startupCallback);
return;
}
LOG.info("Started ZooKeeper");
_executor.schedule(new Runnable() {
@Override
public void run() {
Callback<None> startupCallback = _startupCallback.getAndSet(null);
if (startupCallback != null) {
// Noone has enabled the stores yet either way
LOG.error("No response from ZooKeeper within {}ms, enabling backup stores", _initialZKTimeout);
balancer.enableBackup(startupCallback);
}
}
}, _initialZKTimeout, TimeUnit.MILLISECONDS);
}
});
}
use of com.linkedin.common.callback.Callback in project rest.li by linkedin.
the class TestMIMEChainingMultipleSources method generateSuccessChainCallback.
private Callback<StreamResponse> generateSuccessChainCallback(final ClientMultiPartReceiver receiver) {
return new Callback<StreamResponse>() {
@Override
public void onError(Throwable e) {
Assert.fail();
}
@Override
public void onSuccess(StreamResponse result) {
final MultiPartMIMEReader reader = MultiPartMIMEReader.createAndAcquireStream(result);
reader.registerReaderCallback(receiver);
}
};
}
Aggregations