use of com.linkedin.r2.transport.common.bridge.client.TransportClient in project rest.li by linkedin.
the class SimpleLoadBalancerStateTest method testRefreshWithConcurrentGetTC.
// This test is to verify a fix for a specific bug, where the d2 client receives a zookeeper
// update and concurrent getTrackerClient requests. In that case, all but the first concurrent
// requests got a null tracker client because the degraderLoadBalancerState was not fully initialized
// (hashring was empty), and this continued until the first request had atomically swamped a
// fully initialized state for other requests to use. This test failed on pre-fix code, it now
// succeeds.
@Test(groups = { "small", "back-end" })
public void testRefreshWithConcurrentGetTC() throws URISyntaxException, InterruptedException {
reset();
LinkedList<String> strategyList = new LinkedList<>();
URI uri = URI.create("http://cluster-1/test");
final List<String> schemes = new ArrayList<>();
schemes.add("http");
strategyList.add("degraderV3");
// set up state
_state.listenToService("service-1", new NullStateListenerCallback());
_state.listenToCluster("cluster-1", new NullStateListenerCallback());
assertNull(_state.getStrategy("service-1", "http"));
// Use the _clusterRegistry.put to populate the _state.clusterProperties, used by
// _state.refreshServiceStrategies
_clusterRegistry.put("cluster-1", new ClusterProperties("cluster-1"));
_serviceRegistry.put("service-1", new ServiceProperties("service-1", "cluster-1", "/test", strategyList, Collections.<String, Object>emptyMap(), Collections.<String, Object>emptyMap(), Collections.<String, String>emptyMap(), schemes, Collections.<URI>emptySet()));
LoadBalancerStrategy strategy = _state.getStrategy("service-1", "http");
assertNotNull(strategy, "got null strategy in setup");
// test serial to make sure things are working before concurrent test
TransportClient resultTC = _state.getClient("service-1", "http");
assertNotNull(resultTC, "got null tracker client in non-concurrent env");
ExecutorService myExecutor = Executors.newCachedThreadPool();
ArrayList<TcCallable> cArray = new ArrayList<>();
List<TrackerClient> clients = new ArrayList<>();
Map<Integer, PartitionData> partitionDataMap = new HashMap<>(2);
partitionDataMap.put(DefaultPartitionAccessor.DEFAULT_PARTITION_ID, new PartitionData(1d));
clients.add(new DegraderTrackerClientImpl(uri, partitionDataMap, new DegraderLoadBalancerTest.TestLoadBalancerClient(uri), SystemClock.instance(), null));
for (int i = 0; i < 20; i++) {
cArray.add(i, new TcCallable(clients, _state));
}
Runnable refreshTask = new Runnable() {
@Override
public void run() {
while (true) {
List<String> myStrategyList = new LinkedList<>();
myStrategyList.add("degraderV3");
_state.refreshServiceStrategies(new ServiceProperties("service-1", "cluster-1", "/test", myStrategyList, Collections.<String, Object>emptyMap(), Collections.<String, Object>emptyMap(), Collections.<String, String>emptyMap(), schemes, Collections.<URI>emptySet()));
if (Thread.interrupted()) {
return;
}
}
}
};
myExecutor.execute(refreshTask);
Integer badResults = 0;
ArrayList<Future<Integer>> myList = new ArrayList<>();
for (int i = 0; i < cArray.size(); i++) {
@SuppressWarnings("unchecked") Callable<Integer> c = (Callable) cArray.get(i);
myList.add(i, myExecutor.submit(c));
}
try {
for (int i = 0; i < cArray.size(); i++) {
badResults += myList.get(i).get();
}
} catch (ExecutionException e) {
Assert.assertFalse(true, "got ExecutionException");
} finally {
try {
// call shutdownNow() to send an interrupt to the refreshTask
myExecutor.shutdownNow();
boolean status = myExecutor.awaitTermination(5, TimeUnit.SECONDS);
if (status == false) {
Assert.assertFalse(true, "failed to shutdown threads correctly");
}
} catch (InterruptedException ie) {
// this thread was interrupted
myExecutor.shutdownNow();
}
}
Assert.assertTrue(badResults == 0, "getTrackerClients returned null");
}
use of com.linkedin.r2.transport.common.bridge.client.TransportClient in project rest.li by linkedin.
the class LoadBalancerStrategyTestRunnerBuilder method buildRelativeStrategy.
private LoadBalancerStrategyTestRunner buildRelativeStrategy() {
if (_serviceProperties == null) {
setRelativeLoadBalancerStrategies(new D2RelativeStrategyProperties());
}
_strategy = new RelativeLoadBalancerStrategyFactory(_clockedExecutor, null, new ArrayList<>(), null, _clockedExecutor).newLoadBalancer(_serviceProperties);
_transportClients = _uris.stream().map(uri -> new MockTransportClient(_clockedExecutor, _latencyManager, _errorCountManager, uri, INTERVAL_IN_MILLIS, _currentErrorCountMap, _lastRequestCountMap, _callCountMap, _latencySumMap)).collect(Collectors.toList());
Map<URI, TrackerClient> trackerClientMap = _transportClients.stream().map(transportClient -> {
// If partition map is not specified, by default we only support one partition
Map<Integer, PartitionData> partitionDataMap = _partitionDataMap.getOrDefault(transportClient.getUri(), DEFAULT_PARTITION_DATA_MAP);
return new TrackerClientImpl(transportClient.getUri(), partitionDataMap, transportClient, _clockedExecutor, INTERVAL_IN_MILLIS, (status) -> status >= 500 && status <= 599);
}).collect(Collectors.toMap(TrackerClient::getUri, trackerClient -> trackerClient));
return buildInternal(trackerClientMap);
}
use of com.linkedin.r2.transport.common.bridge.client.TransportClient in project rest.li by linkedin.
the class LoadBalancerStrategyTestRunnerBuilder method buildDegraderStrategy.
private LoadBalancerStrategyTestRunner buildDegraderStrategy() {
if (_serviceProperties == null) {
setDegraderStrategies(new HashMap<>(), new HashMap<>());
}
_strategy = new DegraderLoadBalancerStrategyFactoryV3().newLoadBalancer(_serviceProperties);
_transportClients = _uris.stream().map(uri -> new MockTransportClient(_clockedExecutor, _latencyManager, _errorCountManager, uri, INTERVAL_IN_MILLIS, _currentErrorCountMap, _lastRequestCountMap, _callCountMap, _latencySumMap)).collect(Collectors.toList());
Map<URI, TrackerClient> trackerClientMap = _transportClients.stream().map(transportClient -> {
// If partition map is not specified, by default we only support one partition
Map<Integer, PartitionData> partitionDataMap = _partitionDataMap.getOrDefault(transportClient.getUri(), DEFAULT_PARTITION_DATA_MAP);
return new DegraderTrackerClientImpl(transportClient.getUri(), partitionDataMap, transportClient, _clockedExecutor, DegraderConfigFactory.toDegraderConfig(_serviceProperties.getDegraderProperties()));
}).collect(Collectors.toMap(TrackerClient::getUri, trackerClient -> trackerClient));
return buildInternal(trackerClientMap);
}
use of com.linkedin.r2.transport.common.bridge.client.TransportClient in project rest.li by linkedin.
the class LatencyInstrumentationResource method create.
/**
* This is the "upstream endpoint" which is queried directly by the integration test.
* This endpoint makes a call to {@link #batchPartialUpdate(BatchPatchRequest)} (the "downstream endpoint"),
* then packs all the client-side timing data into the original server-side request context.
*/
@ReturnEntity
@RestMethod.Create
public CreateKVResponse<Long, InstrumentationControl> create(InstrumentationControl control) {
final boolean forceException = control.isForceException();
final boolean useScatterGather = control.isUseScatterGather();
final String uriPrefix = control.getServiceUriPrefix();
// Build the downstream request
final BatchPartialUpdateEntityRequestBuilder<Long, InstrumentationControl> builder = new LatencyInstrumentationBuilders().batchPartialUpdateAndGet();
final PatchRequest<InstrumentationControl> patch = PatchGenerator.diffEmpty(control);
for (long i = 0; i < DOWNSTREAM_BATCH_SIZE; i++) {
builder.input(i, patch);
}
final BatchPartialUpdateEntityRequest<Long, InstrumentationControl> request = builder.build();
// Set up the Rest.li client config
final RestLiClientConfig clientConfig = new RestLiClientConfig();
clientConfig.setUseStreaming(control.isUseStreaming());
if (useScatterGather) {
clientConfig.setScatterGatherStrategy(new DefaultScatterGatherStrategy(new DummyUriMapper()));
}
final TransportClient transportClient = new HttpClientFactory.Builder().build().getClient(Collections.emptyMap());
final RestClient restClient = new ForceScatterGatherRestClient(new TransportClientAdapter(transportClient), uriPrefix, clientConfig);
final RequestContext serverRequestContext = getContext().getRawRequestContext();
final RequestContext clientRequestContext = new RequestContext();
// Load the timing importance threshold from the server context into the client context
clientRequestContext.putLocalAttr(TimingContextUtil.TIMING_IMPORTANCE_THRESHOLD_KEY_NAME, serverRequestContext.getLocalAttr(TimingContextUtil.TIMING_IMPORTANCE_THRESHOLD_KEY_NAME));
try {
// Make the request, then assert that the returned errors (if any) are as expected
BatchKVResponse<Long, UpdateEntityStatus<InstrumentationControl>> response = restClient.sendRequest(request, clientRequestContext).getResponseEntity();
final Map<Long, ErrorResponse> errors = response.getErrors();
if (forceException && errors.isEmpty()) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Expected failures for the downstream batch request, but found none.");
}
if (!forceException && !errors.isEmpty()) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Expected no failures for the downstream batch request, but found some.");
}
for (ErrorResponse errorResponse : errors.values()) {
if (!DOWNSTREAM_ERROR_CODE.equals(errorResponse.getCode())) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Encountered a downstream failure with an unexpected or missing error code.");
}
}
} catch (RemoteInvocationException e) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Downstream failures should be batch entry failures, but encountered a top-level request failure.", e);
}
Map<TimingKey, TimingContextUtil.TimingContext> clientTimingsMap = TimingContextUtil.getTimingsMap(clientRequestContext);
Map<TimingKey, TimingContextUtil.TimingContext> serverTimingsMap = TimingContextUtil.getTimingsMap(serverRequestContext);
// Load all client timings into the server timings map
serverTimingsMap.putAll(clientTimingsMap);
getContext().setResponseHeader(HAS_CLIENT_TIMINGS_HEADER, Boolean.TRUE.toString());
if (forceException) {
throw new RestLiServiceException(HttpStatus.S_400_BAD_REQUEST, "You wanted me to fail, so I failed.").setCode(UPSTREAM_ERROR_CODE);
}
return new CreateKVResponse<>(1L, control);
}
use of com.linkedin.r2.transport.common.bridge.client.TransportClient in project rest.li by linkedin.
the class DynamicClient method getClientCallback.
private Callback<TransportClient> getClientCallback(Request request, RequestContext requestContext, final boolean restOverStream, Callback<? extends Response> callback, SuccessCallback<Client> clientSuccessCallback) {
return new Callback<TransportClient>() {
@Override
public void onError(Throwable e) {
TimingContextUtil.markTiming(requestContext, TIMING_KEY);
callback.onError(e);
warn(_log, "unable to find service for: ", extractLogInfo(request));
}
@Override
public void onSuccess(TransportClient client) {
TimingContextUtil.markTiming(requestContext, TIMING_KEY);
if (client != null) {
clientSuccessCallback.onSuccess(new TransportClientAdapter(client, restOverStream));
} else {
callback.onError(new ServiceUnavailableException("PEGA_1000. Unknown: " + request.getURI(), "got null client from load balancer"));
}
}
};
}
Aggregations