use of com.linkedin.r2.message.timing.TimingKey in project rest.li by linkedin.
the class TestSslTimingKey method testSslTimingKey.
@Test
public void testSslTimingKey() throws Exception {
if (isHttp2StreamBasedChannel())
return;
final EchoService client = new RestEchoClient(Bootstrap.createURI(_port, Bootstrap.getEchoURI(), true), createClient());
final String msg = "This is a simple http echo message";
final FutureCallback<String> callback = new FutureCallback<>();
client.echo(msg, callback);
Assert.assertEquals(callback.get(), msg);
RequestContext context = _clientCaptureFilter.getRequestContext();
@SuppressWarnings("unchecked") Map<TimingKey, TimingContext> map = (Map<TimingKey, TimingContext>) context.getLocalAttr("timings");
Assert.assertNotNull(map);
Assert.assertTrue(map.containsKey(SslHandshakeTimingHandler.TIMING_KEY));
TimingContext timingContext = map.get(SslHandshakeTimingHandler.TIMING_KEY);
Assert.assertNotNull(timingContext);
}
use of com.linkedin.r2.message.timing.TimingKey in project rest.li by linkedin.
the class TestLatencyInstrumentation method checkTimingKeyCompleteness.
/**
* Ensures that the recorded timing keys are "complete", meaning that all keys which are expected to be present
* are present. Also ensures that no unexpected keys are present.
* @param forceException parameter from the test method
* @param timingImportanceThreshold parameter from the test method
*/
private void checkTimingKeyCompleteness(boolean forceException, TimingImportance timingImportanceThreshold, boolean useScatterGather) {
// Form the set of expected timing keys using the current test parameters
Set<TimingKey> expectedKeys = Arrays.stream(FrameworkTimingKeys.values()).map(FrameworkTimingKeys::key).filter(timingKey -> !(useScatterGather && timingKey.getName().startsWith(FrameworkTimingKeys.KEY_PREFIX + "client"))).filter(timingKey -> {
if (forceException) {
return !TIMING_KEYS_MISSING_ON_ERROR.contains(timingKey);
} else {
return !TIMING_KEYS_MISSING_ON_SUCCESS.contains(timingKey);
}
}).filter(timingKey -> !TIMING_KEYS_MISSING_ON_PROTOCOL_2_0_0.contains(timingKey)).filter(timingKey -> timingImportanceThreshold == null || TIMING_KEYS_ALWAYS_PRESENT.contains(timingKey) || timingKey.getTimingImportance().isAtLeast(timingImportanceThreshold)).collect(Collectors.toSet());
// Check that all keys have complete timings (not -1) and that there are no unexpected keys
for (TimingKey timingKey : _resultMap.keySet()) {
if (expectedKeys.contains(timingKey)) {
expectedKeys.remove(timingKey);
Assert.assertNotEquals(_resultMap.get(timingKey).getDurationNano(), -1, timingKey.getName() + " is -1");
} else if (timingKey.getName().contains(FrameworkTimingKeys.KEY_PREFIX)) {
Assert.fail("Encountered unexpected key: " + timingKey);
}
}
// Check that the set of recorded timing keys is "complete"
Assert.assertTrue(expectedKeys.isEmpty(), "Missing keys: " + expectedKeys.stream().map(key -> String.format("\"%s\"", key.getName())).collect(Collectors.joining(", ")));
}
use of com.linkedin.r2.message.timing.TimingKey in project rest.li by linkedin.
the class TestTimingCallback method testOrdering.
/**
* Ensures that timing keys are marked in the same order that they are added.
*/
@Test
public void testOrdering() {
final RequestContext requestContext = new RequestContext();
final Callback<Long> callback = new Callback<Long>() {
@Override
public void onSuccess(Long result) {
Map<TimingKey, TimingContextUtil.TimingContext> timings = TimingContextUtil.getTimingsMap(requestContext);
// Ensure all keys are present
Assert.assertTrue(timings.containsKey(KEY_H));
Assert.assertTrue(timings.containsKey(KEY_M));
Assert.assertTrue(timings.containsKey(KEY_L));
// Ensure timing start times/durations are consistent based on their ordering in the callback
TimingContextUtil.TimingContext contextH = timings.get(KEY_H);
TimingContextUtil.TimingContext contextM = timings.get(KEY_M);
TimingContextUtil.TimingContext contextL = timings.get(KEY_L);
Assert.assertTrue(contextM.getStartTimeNano() < contextL.getStartTimeNano());
Assert.assertTrue(contextL.getStartTimeNano() < contextH.getStartTimeNano());
Assert.assertTrue(contextL.getDurationNano() < contextM.getDurationNano());
Assert.assertTrue(contextH.getDurationNano() < contextM.getDurationNano());
}
@Override
public void onError(Throwable e) {
}
};
final Callback<Long> timingCallback = new TimingCallback.Builder<>(callback, requestContext).addBeginTimingKey(KEY_M).addBeginTimingKey(KEY_L).addEndTimingKey(KEY_L).addBeginTimingKey(KEY_H).addEndTimingKey(KEY_H).addEndTimingKey(KEY_M).build();
timingCallback.onSuccess(1L);
}
use of com.linkedin.r2.message.timing.TimingKey in project rest.li by linkedin.
the class TestTimingCallback method testBuilder.
/**
* Ensures that the builder can correctly determine how to filter out timing keys based on the current timing
* importance threshold, and that it can correctly determine when to return the original callback rather than wrapping
* it with a new one.
* @param timingImportanceThreshold timing importance threshold
*/
@Test(dataProvider = "timingImportanceThreshold")
public void testBuilder(TimingImportance timingImportanceThreshold) {
final RequestContext requestContext = new RequestContext();
if (timingImportanceThreshold != null) {
requestContext.putLocalAttr(TimingContextUtil.TIMING_IMPORTANCE_THRESHOLD_KEY_NAME, timingImportanceThreshold);
}
final Callback<Long> callback = new Callback<Long>() {
@Override
public void onSuccess(Long result) {
Map<TimingKey, TimingContextUtil.TimingContext> timings = TimingContextUtil.getTimingsMap(requestContext);
// Ensure that keys have been filtered out correctly
if (timingImportanceThreshold == null || TimingImportance.LOW.isAtLeast(timingImportanceThreshold)) {
Assert.assertTrue(timings.containsKey(KEY_L));
Assert.assertTrue(timings.containsKey(KEY_M));
} else if (TimingImportance.MEDIUM.isAtLeast(timingImportanceThreshold)) {
Assert.assertFalse(timings.containsKey(KEY_L));
Assert.assertTrue(timings.containsKey(KEY_M));
} else {
Assert.assertFalse(timings.containsKey(KEY_L));
Assert.assertFalse(timings.containsKey(KEY_M));
}
}
@Override
public void onError(Throwable e) {
}
};
final Callback<Long> timingCallback = new TimingCallback.Builder<>(callback, requestContext).addBeginTimingKey(KEY_L).addBeginTimingKey(KEY_M).addEndTimingKey(KEY_L).addEndTimingKey(KEY_M).build();
// Ensure that the builder can correctly determine when to return the original callback
if (timingImportanceThreshold == null || !timingImportanceThreshold.equals(TimingImportance.HIGH)) {
Assert.assertTrue(timingCallback instanceof TimingCallback);
} else {
Assert.assertFalse(timingCallback instanceof TimingCallback);
}
timingCallback.onSuccess(1L);
}
use of com.linkedin.r2.message.timing.TimingKey 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);
}
Aggregations