Skip to content

Commit

Permalink
Add instrumentation timing markers for projection mask application
Browse files Browse the repository at this point in the history
As part of the latency instrumentation initiative, this commit adds timing
markers for projection mask application code paths.

RB=1745924
G=sf-reviewers
R=dmessink,fcapponi,ssheng,bsoetarm,crzhang
A=kbalasub,bsoetarm
  • Loading branch information
evanw555 committed Dec 10, 2019
1 parent 601a5ed commit 67c7ebd
Show file tree
Hide file tree
Showing 18 changed files with 114 additions and 17 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
28.0.13
-------
(RB=1745924)
Add instrumentation timing markers for projection mask application

(RB=1745918)
Add support for framework latency instrumentation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@

package com.linkedin.restli.internal.server.response;


import com.linkedin.data.DataMap;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.jersey.api.uri.UriBuilder;
import com.linkedin.jersey.api.uri.UriComponent;
import com.linkedin.r2.message.Request;
import com.linkedin.r2.message.timing.FrameworkTimingKeys;
import com.linkedin.r2.message.timing.TimingContextUtil;
import com.linkedin.restli.common.BatchCreateIdResponse;
import com.linkedin.restli.common.CreateIdEntityStatus;
import com.linkedin.restli.common.CreateIdStatus;
Expand All @@ -30,18 +31,16 @@
import com.linkedin.restli.common.RestConstants;
import com.linkedin.restli.internal.common.ProtocolVersionUtil;
import com.linkedin.restli.internal.common.URIParamUtils;
import com.linkedin.restli.internal.server.methods.AnyRecord;
import com.linkedin.restli.internal.server.RoutingResult;
import com.linkedin.restli.internal.server.methods.AnyRecord;
import com.linkedin.restli.internal.server.util.RestUtils;
import com.linkedin.restli.server.BatchCreateKVResult;
import com.linkedin.restli.server.BatchCreateResult;
import com.linkedin.restli.server.CreateResponse;
import com.linkedin.restli.server.CreateKVResponse;
import com.linkedin.restli.server.CreateResponse;
import com.linkedin.restli.server.ResourceContext;
import com.linkedin.restli.server.RestLiResponseData;
import com.linkedin.restli.server.RestLiServiceException;
import com.linkedin.restli.server.ResourceContext;


import java.net.HttpCookie;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -122,6 +121,9 @@ public RestLiResponseData<BatchCreateResponseEnvelope> buildRestLiResponseData(R
}
List<BatchCreateResponseEnvelope.CollectionCreateResponseItem> collectionCreateList = new ArrayList<>(list.getResults().size());

TimingContextUtil.beginTiming(routingResult.getContext().getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

for (CreateKVResponse<?, ?> createKVResponse : list.getResults())
{
if (createKVResponse == null)
Expand All @@ -136,6 +138,7 @@ public RestLiResponseData<BatchCreateResponseEnvelope> buildRestLiResponseData(R
if (createKVResponse.getError() == null)
{
DataMap entityData = createKVResponse.getEntity() != null ? createKVResponse.getEntity().data() : null;

final DataMap data = RestUtils.projectFields(entityData,
resourceContext.getProjectionMode(),
resourceContext.getProjectionMask());
Expand All @@ -156,6 +159,10 @@ public RestLiResponseData<BatchCreateResponseEnvelope> buildRestLiResponseData(R
}
}
}

TimingContextUtil.endTiming(routingResult.getContext().getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

return new RestLiResponseDataImpl<>(new BatchCreateResponseEnvelope(HttpStatus.S_200_OK, collectionCreateList, true), headers, cookies);
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import com.linkedin.data.collections.CheckedUtil;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.r2.message.Request;
import com.linkedin.r2.message.timing.FrameworkTimingKeys;
import com.linkedin.r2.message.timing.TimingContextUtil;
import com.linkedin.restli.common.BatchCollectionResponse;
import com.linkedin.restli.common.CollectionMetadata;
import com.linkedin.restli.common.CollectionResponse;
Expand All @@ -36,7 +38,6 @@
import com.linkedin.restli.server.ProjectionMode;
import com.linkedin.restli.server.RestLiResponseData;
import com.linkedin.restli.server.RestLiServiceException;

import java.net.HttpCookie;
import java.net.URI;
import java.util.ArrayList;
Expand Down Expand Up @@ -100,6 +101,10 @@ public RestLiResponseData<BatchFinderResponseEnvelope> buildRestLiResponseData(R
List<BatchFinderEntry> collectionResponse = new ArrayList<>(criteriaParams.size());

final ResourceContextImpl resourceContext = (ResourceContextImpl) routingResult.getContext();

TimingContextUtil.beginTiming(routingResult.getContext().getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

for (Object criteriaParam : criteriaParams.values())
{
RecordTemplate criteria = new AnyRecord((DataMap) criteriaParam);
Expand All @@ -114,8 +119,10 @@ public RestLiResponseData<BatchFinderResponseEnvelope> buildRestLiResponseData(R
//Process paging
final CollectionMetadata projectedPaging =
buildPaginationMetaData(routingResult, criteria, resourceContext, request, cr);

//Process metadata
final AnyRecord projectedCustomMetadata = buildMetaData(cr, resourceContext);

entry = new BatchFinderEntry(elements, projectedPaging, projectedCustomMetadata);
}
else if (result.getErrors().containsKey(criteria))
Expand All @@ -131,6 +138,9 @@ else if (result.getErrors().containsKey(criteria))
collectionResponse.add(entry);
}

TimingContextUtil.endTiming(routingResult.getContext().getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

return new RestLiResponseDataImpl<>(new BatchFinderResponseEnvelope(HttpStatus.S_200_OK, collectionResponse),
headers,
cookies);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,25 @@

package com.linkedin.restli.internal.server.response;


import com.linkedin.data.DataMap;
import com.linkedin.data.collections.CheckedUtil;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.data.template.SetMode;
import com.linkedin.r2.message.Request;
import com.linkedin.r2.message.timing.FrameworkTimingKeys;
import com.linkedin.r2.message.timing.TimingContextUtil;
import com.linkedin.restli.common.BatchResponse;
import com.linkedin.restli.common.EntityResponse;
import com.linkedin.restli.common.HttpStatus;
import com.linkedin.restli.common.ProtocolVersion;
import com.linkedin.restli.internal.common.URIParamUtils;
import com.linkedin.restli.internal.server.response.BatchResponseEnvelope.BatchResponseEntry;
import com.linkedin.restli.internal.server.RoutingResult;
import com.linkedin.restli.internal.server.methods.AnyRecord;
import com.linkedin.restli.internal.server.response.BatchResponseEnvelope.BatchResponseEntry;
import com.linkedin.restli.internal.server.util.RestUtils;
import com.linkedin.restli.server.BatchResult;
import com.linkedin.restli.server.RestLiResponseData;
import com.linkedin.restli.server.RestLiServiceException;

import java.net.HttpCookie;
import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -163,6 +163,9 @@ public RestLiResponseData<BatchGetResponseEnvelope> buildRestLiResponseData(Requ
// In this case it is OK to swallow this exception and proceed.
}

TimingContextUtil.beginTiming(routingResult.getContext().getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

Map<Object, BatchResponseEntry> batchResult = new HashMap<>(entities.size() + serviceErrors.size());
for (Map.Entry<Object, RecordTemplate> entity : entities.entrySet())
{
Expand All @@ -177,10 +180,14 @@ public RestLiResponseData<BatchGetResponseEnvelope> buildRestLiResponseData(Requ
final DataMap projectedData = RestUtils.projectFields(entity.getValue().data(),
routingResult.getContext().getProjectionMode(),
routingResult.getContext().getProjectionMask());

AnyRecord anyRecord = new AnyRecord(projectedData);
batchResult.put(finalKey, new BatchResponseEntry(statuses.get(entity.getKey()), anyRecord));
}

TimingContextUtil.endTiming(routingResult.getContext().getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

for (Map.Entry<Object, RestLiServiceException> entity : serviceErrors.entrySet())
{
if (entity.getKey() == null || entity.getValue() == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,30 @@

package com.linkedin.restli.internal.server.response;


import com.linkedin.data.DataMap;
import com.linkedin.data.collections.CheckedUtil;
import com.linkedin.r2.message.Request;
import com.linkedin.r2.message.timing.FrameworkTimingKeys;
import com.linkedin.r2.message.timing.TimingContextUtil;
import com.linkedin.restli.common.BatchResponse;
import com.linkedin.restli.internal.server.response.BatchResponseEnvelope.BatchResponseEntry;
import com.linkedin.restli.common.HttpStatus;
import com.linkedin.restli.common.ProtocolVersion;
import com.linkedin.restli.common.UpdateStatus;
import com.linkedin.restli.internal.common.URIParamUtils;
import com.linkedin.restli.internal.server.RoutingResult;
import com.linkedin.restli.internal.server.methods.AnyRecord;
import com.linkedin.restli.internal.server.response.BatchResponseEnvelope.BatchResponseEntry;
import com.linkedin.restli.server.BatchUpdateResult;
import com.linkedin.restli.server.ResourceContext;
import com.linkedin.restli.server.RestLiResponseData;
import com.linkedin.restli.server.RestLiServiceException;
import com.linkedin.restli.server.UpdateResponse;

import java.net.HttpCookie;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
* This is the base implementation for {@link RestLiResponseBuilder}s for BATCH_UPDATE, BATCH_PARTIAL_UPDATE, and
* BATCH_DELETE responses.
Expand Down Expand Up @@ -110,6 +111,9 @@ public D buildRestLiResponseData(Request request,
+ routingResult.getResourceMethod());
}

TimingContextUtil.beginTiming(routingResult.getContext().getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

Map<Object, BatchResponseEntry> batchResponseMap = new HashMap<>();
for (Map.Entry<Object, UpdateResponse> entry : results.entrySet())
{
Expand All @@ -123,11 +127,16 @@ public D buildRestLiResponseData(Request request,
if (!serviceErrors.containsKey(entry.getKey()))
{
Object finalKey = ResponseUtils.translateCanonicalKeyToAlternativeKeyIfNeeded(entry.getKey(), routingResult);

UpdateStatus updateStatus = buildUpdateStatus(routingResult.getContext(), entry.getValue());

batchResponseMap.put(finalKey, new BatchResponseEntry(entry.getValue().getStatus(), updateStatus));
}
}

TimingContextUtil.endTiming(routingResult.getContext().getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

for (Map.Entry<Object, RestLiServiceException> entry : serviceErrors.entrySet())
{
if (entry.getKey() == null || entry.getValue() == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import com.linkedin.data.collections.CheckedUtil;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.r2.message.Request;
import com.linkedin.r2.message.timing.FrameworkTimingKeys;
import com.linkedin.r2.message.timing.TimingContextUtil;
import com.linkedin.restli.common.CollectionMetadata;
import com.linkedin.restli.common.CollectionResponse;
import com.linkedin.restli.common.HttpStatus;
Expand Down Expand Up @@ -122,6 +124,9 @@ private D buildRestLiResponseData(final Request request,
RestUtils.buildMetadata(request.getURI(), resourceContext, routingResult.getResourceMethod(),
elements, pageIncrement, totalResults);

TimingContextUtil.beginTiming(resourceContext.getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

//PagingMetadata cannot be null at this point so we skip the null check. Notice here that we are using automatic
//intentionally since resource methods cannot explicitly project paging. However, it should be noted that client
//resource methods have the option of selectively setting the total to null. This happens if a client decides
Expand Down Expand Up @@ -165,6 +170,9 @@ private D buildRestLiResponseData(final Request request,
projectedCustomMetadata = null;
}

TimingContextUtil.endTiming(resourceContext.getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

return buildResponseData(HttpStatus.S_200_OK, processedElements, projectedPaging, projectedCustomMetadata, headers, cookies);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import com.linkedin.jersey.api.uri.UriBuilder;
import com.linkedin.jersey.api.uri.UriComponent;
import com.linkedin.r2.message.Request;
import com.linkedin.r2.message.timing.FrameworkTimingKeys;
import com.linkedin.r2.message.timing.TimingContextUtil;
import com.linkedin.restli.common.HttpStatus;
import com.linkedin.restli.common.IdResponse;
import com.linkedin.restli.common.ProtocolVersion;
Expand Down Expand Up @@ -117,7 +119,15 @@ public RestLiResponseData<CreateResponseEnvelope> buildRestLiResponseData(Reques
}

DataMap entityData = entity.data();

TimingContextUtil.beginTiming(resourceContext.getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

final DataMap data = RestUtils.projectFields(entityData, resourceContext.getProjectionMode(), resourceContext.getProjectionMask());

TimingContextUtil.endTiming(resourceContext.getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

idResponse = new AnyRecord(data);
// Ideally, we should set an IdEntityResponse to the envelope. But we are keeping AnyRecord
// to make sure the runtime object is backwards compatible.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import com.linkedin.data.DataMap;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.r2.message.Request;
import com.linkedin.r2.message.timing.FrameworkTimingKeys;
import com.linkedin.r2.message.timing.TimingContextUtil;
import com.linkedin.restli.common.HttpStatus;
import com.linkedin.restli.internal.server.RoutingResult;
import com.linkedin.restli.internal.server.methods.AnyRecord;
Expand Down Expand Up @@ -73,9 +75,16 @@ record = (RecordTemplate) result;
status = HttpStatus.S_200_OK;
}
final ResourceContext resourceContext = routingResult.getContext();

TimingContextUtil.beginTiming(resourceContext.getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

final DataMap data = RestUtils.projectFields(record.data(), resourceContext.getProjectionMode(),
resourceContext.getProjectionMask());

TimingContextUtil.endTiming(resourceContext.getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

return new RestLiResponseDataImpl<>(new GetResponseEnvelope(status, new AnyRecord(data)), headers, cookies);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import com.linkedin.data.DataMap;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.r2.message.Request;
import com.linkedin.r2.message.timing.FrameworkTimingKeys;
import com.linkedin.r2.message.timing.TimingContextUtil;
import com.linkedin.restli.common.EntityResponse;
import com.linkedin.restli.common.HttpStatus;
import com.linkedin.restli.internal.server.ResponseType;
Expand Down Expand Up @@ -81,7 +83,15 @@ public RestLiResponseData<PartialUpdateResponseEnvelope> buildRestLiResponseData
if (updateEntityResponse.hasEntity())
{
DataMap entityData = updateEntityResponse.getEntity().data();

TimingContextUtil.beginTiming(resourceContext.getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

final DataMap data = RestUtils.projectFields(entityData, resourceContext.getProjectionMode(), resourceContext.getProjectionMask());

TimingContextUtil.endTiming(resourceContext.getRawRequestContext(),
FrameworkTimingKeys.SERVER_RESPONSE_RESTLI_PROJECTION_APPLY.key());

// Returned entity is to be added to the response envelope
entityResponse = new EntityResponse<>(data, updateEntityResponse.getEntity().getClass());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.linkedin.data.transform.filter.request.MaskTree;
import com.linkedin.pegasus.generator.examples.Foo;
import com.linkedin.pegasus.generator.examples.Fruits;
import com.linkedin.r2.message.RequestContext;
import com.linkedin.r2.message.rest.RestRequest;
import com.linkedin.r2.message.rest.RestRequestBuilder;
import com.linkedin.restli.common.BatchCreateIdResponse;
Expand Down Expand Up @@ -275,6 +276,7 @@ public void testReturnEntityInBuildRestLiResponseData(Object batchCreateResult,
EasyMock.expect(mockContext.isReturnEntityRequested()).andReturn(isReturnEntityRequested);
EasyMock.expect(mockContext.getProjectionMode()).andReturn(ProjectionMode.AUTOMATIC);
EasyMock.expect(mockContext.getProjectionMask()).andReturn(null);
EasyMock.expect(mockContext.getRawRequestContext()).andReturn(new RequestContext()).anyTimes();
EasyMock.replay(mockContext);

ResourceMethodDescriptor mockDescriptor = getMockResourceMethodDescriptor(null);
Expand Down Expand Up @@ -321,6 +323,7 @@ public void testProjectionInBuildRestLiResponseData() throws URISyntaxException
EasyMock.expect(mockContext.isReturnEntityRequested()).andReturn(true);
EasyMock.expect(mockContext.getProjectionMode()).andReturn(ProjectionMode.AUTOMATIC);
EasyMock.expect(mockContext.getProjectionMask()).andReturn(maskTree);
EasyMock.expect(mockContext.getRawRequestContext()).andReturn(new RequestContext()).anyTimes();
EasyMock.replay(mockContext);

ResourceMethodDescriptor mockDescriptor = getMockResourceMethodDescriptor(null);
Expand Down Expand Up @@ -398,6 +401,7 @@ private static ServerResourceContext getMockKVResourceContext(String altKeyName)
EasyMock.expect(mockContext.getProjectionMask()).andReturn(null).atLeastOnce();
Map<String, String> protocolVersionOnlyHeaders = Collections.singletonMap(RestConstants.HEADER_RESTLI_PROTOCOL_VERSION, AllProtocolVersions.RESTLI_PROTOCOL_2_0_0.getProtocolVersion().toString());
EasyMock.expect(mockContext.getRequestHeaders()).andReturn(protocolVersionOnlyHeaders).atLeastOnce();
EasyMock.expect(mockContext.getRawRequestContext()).andReturn(new RequestContext()).anyTimes();

EasyMock.replay(mockContext);
return mockContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.linkedin.jersey.api.uri.UriComponent;
import com.linkedin.pegasus.generator.examples.Foo;
import com.linkedin.pegasus.generator.examples.Fruits;
import com.linkedin.r2.message.RequestContext;
import com.linkedin.r2.message.rest.RestRequest;
import com.linkedin.r2.message.rest.RestRequestBuilder;
import com.linkedin.restli.common.CollectionMetadata;
Expand Down Expand Up @@ -213,6 +214,7 @@ private static RoutingResult getMockRoutingResult(List<Criteria> criteria, Proto
EasyMock.expect(mockContext.getProjectionMask()).andStubReturn(mockMask);
EasyMock.expect(mockContext.getMetadataProjectionMask()).andStubReturn(mockMask);
EasyMock.expect(mockContext.getMetadataProjectionMode()).andStubReturn(ProjectionMode.MANUAL);
EasyMock.expect(mockContext.getRawRequestContext()).andStubReturn(new RequestContext());

EasyMock.replay(mockContext);

Expand Down
Loading

0 comments on commit 67c7ebd

Please sign in to comment.