Code review comment: Shouldn't we have separate checks for these? In case decode started, and never finished. It may provide insights on gaps in the SDK or decode process or any internal errors. Like we have for other events. where we see null end time. 

Code before review:
private void messageReceived(final ChannelHandlerContext context, final RntbdResponse response) {
final Long transportRequestId = response.getTransportRequestId();
if (transportRequestId == null) {
reportIssue(context, "response ignored because its transportRequestId is missing: {}", response);
return;
}
final RntbdRequestRecord requestRecord = this.pendingRequests.get(transportRequestId);
if (requestRecord == null) {
logger.debug("response {} ignored because its requestRecord is missing: {}", transportRequestId, response);
return;
}
if (response.getDecodeEndTime() != null && response.getDecodeStartTime() != null) {
requestRecord.stage(RntbdRequestRecord.Stage.DECODE_STARTED, response.getDecodeStartTime());
requestRecord.stage(RntbdRequestRecord.Stage.DECODE_COMPLETED, response.getDecodeEndTime());
}
requestRecord.responseLength(response.getMessageLength());
requestRecord.stage(RntbdRequestRecord.Stage.RECEIVED);
final HttpResponseStatus status = response.getStatus();
final UUID activityId = response.getActivityId();
final int statusCode = status.code();
if ((HttpResponseStatus.OK.code() <= statusCode && statusCode < HttpResponseStatus.MULTIPLE_CHOICES.code()) ||
statusCode == HttpResponseStatus.NOT_MODIFIED.code()) {
final StoreResponse storeResponse = response.toStoreResponse(this.contextFuture.getNow(null));
requestRecord.complete(storeResponse);
} else {
final CosmosException cause;
final long lsn = response.getHeader(RntbdResponseHeader.LSN);
final String partitionKeyRangeId = response.getHeader(RntbdResponseHeader.PartitionKeyRangeId);
final CosmosError error = response.hasPayload()
? new CosmosError(RntbdObjectMapper.readTree(response))
: new CosmosError(Integer.toString(statusCode), status.reasonPhrase(), status.codeClass().name());
final Map<String, String> responseHeaders = response.getHeaders().asMap(
this.rntbdContext().orElseThrow(IllegalStateException::new), activityId
);
final String resourceAddress = requestRecord.args().physicalAddress() != null ?
requestRecord.args().physicalAddress().toString() : null;
switch (status.code()) {
case StatusCodes.BADREQUEST:
cause = new BadRequestException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.CONFLICT:
cause = new ConflictException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.FORBIDDEN:
cause = new ForbiddenException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.GONE:
final int subStatusCode = Math.toIntExact(response.getHeader(RntbdResponseHeader.SubStatus));
switch (subStatusCode) {
case SubStatusCodes.COMPLETING_SPLIT:
cause = new PartitionKeyRangeIsSplittingException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case SubStatusCodes.COMPLETING_PARTITION_MIGRATION:
cause = new PartitionIsMigratingException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case SubStatusCodes.NAME_CACHE_IS_STALE:
cause = new InvalidPartitionException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case SubStatusCodes.PARTITION_KEY_RANGE_GONE:
cause = new PartitionKeyRangeGoneException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
default:
GoneException goneExceptionFromService =
new GoneException(error, lsn, partitionKeyRangeId, responseHeaders);
goneExceptionFromService.setIsBasedOn410ResponseFromService();
cause = goneExceptionFromService;
break;
}
break;
case StatusCodes.INTERNAL_SERVER_ERROR:
cause = new InternalServerErrorException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.LOCKED:
cause = new LockedException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.METHOD_NOT_ALLOWED:
cause = new MethodNotAllowedException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.NOTFOUND:
cause = new NotFoundException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.PRECONDITION_FAILED:
cause = new PreconditionFailedException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.REQUEST_ENTITY_TOO_LARGE:
cause = new RequestEntityTooLargeException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.REQUEST_TIMEOUT:
Exception inner = new RequestTimeoutException(error, lsn, partitionKeyRangeId, responseHeaders);
cause = new GoneException(resourceAddress, error, lsn, partitionKeyRangeId, responseHeaders, inner);
break;
case StatusCodes.RETRY_WITH:
cause = new RetryWithException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.SERVICE_UNAVAILABLE:
cause = new ServiceUnavailableException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.TOO_MANY_REQUESTS:
cause = new RequestRateTooLargeException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.UNAUTHORIZED:
cause = new UnauthorizedException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
default:
cause = BridgeInternal.createCosmosException(resourceAddress, status.code(), error, responseHeaders);
break;
}
BridgeInternal.setResourceAddress(cause, resourceAddress);
requestRecord.completeExceptionally(cause);
}
}

Line under review: if (response.getDecodeEndTime() != null && response.getDecodeStartTime() != null) {

Code after addressing the review:
private void messageReceived(final ChannelHandlerContext context, final RntbdResponse response) {
final Long transportRequestId = response.getTransportRequestId();
if (transportRequestId == null) {
reportIssue(context, "response ignored because its transportRequestId is missing: {}", response);
return;
}
final RntbdRequestRecord requestRecord = this.pendingRequests.get(transportRequestId);
if (requestRecord == null) {
logger.debug("response {} ignored because its requestRecord is missing: {}", transportRequestId, response);
return;
}
requestRecord.stage(RntbdRequestRecord.Stage.DECODE_STARTED, response.getDecodeStartTime());
requestRecord.stage(
RntbdRequestRecord.Stage.RECEIVED,
response.getDecodeEndTime() != null ? response.getDecodeEndTime() : Instant.now());
requestRecord.responseLength(response.getMessageLength());
final HttpResponseStatus status = response.getStatus();
final UUID activityId = response.getActivityId();
final int statusCode = status.code();
if ((HttpResponseStatus.OK.code() <= statusCode && statusCode < HttpResponseStatus.MULTIPLE_CHOICES.code()) ||
statusCode == HttpResponseStatus.NOT_MODIFIED.code()) {
final StoreResponse storeResponse = response.toStoreResponse(this.contextFuture.getNow(null));
requestRecord.complete(storeResponse);
} else {
final CosmosException cause;
final long lsn = response.getHeader(RntbdResponseHeader.LSN);
final String partitionKeyRangeId = response.getHeader(RntbdResponseHeader.PartitionKeyRangeId);
final CosmosError error = response.hasPayload()
? new CosmosError(RntbdObjectMapper.readTree(response))
: new CosmosError(Integer.toString(statusCode), status.reasonPhrase(), status.codeClass().name());
final Map<String, String> responseHeaders = response.getHeaders().asMap(
this.rntbdContext().orElseThrow(IllegalStateException::new), activityId
);
final String resourceAddress = requestRecord.args().physicalAddress() != null ?
requestRecord.args().physicalAddress().toString() : null;
switch (status.code()) {
case StatusCodes.BADREQUEST:
cause = new BadRequestException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.CONFLICT:
cause = new ConflictException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.FORBIDDEN:
cause = new ForbiddenException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.GONE:
final int subStatusCode = Math.toIntExact(response.getHeader(RntbdResponseHeader.SubStatus));
switch (subStatusCode) {
case SubStatusCodes.COMPLETING_SPLIT:
cause = new PartitionKeyRangeIsSplittingException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case SubStatusCodes.COMPLETING_PARTITION_MIGRATION:
cause = new PartitionIsMigratingException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case SubStatusCodes.NAME_CACHE_IS_STALE:
cause = new InvalidPartitionException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case SubStatusCodes.PARTITION_KEY_RANGE_GONE:
cause = new PartitionKeyRangeGoneException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
default:
GoneException goneExceptionFromService =
new GoneException(error, lsn, partitionKeyRangeId, responseHeaders);
goneExceptionFromService.setIsBasedOn410ResponseFromService();
cause = goneExceptionFromService;
break;
}
break;
case StatusCodes.INTERNAL_SERVER_ERROR:
cause = new InternalServerErrorException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.LOCKED:
cause = new LockedException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.METHOD_NOT_ALLOWED:
cause = new MethodNotAllowedException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.NOTFOUND:
cause = new NotFoundException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.PRECONDITION_FAILED:
cause = new PreconditionFailedException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.REQUEST_ENTITY_TOO_LARGE:
cause = new RequestEntityTooLargeException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.REQUEST_TIMEOUT:
Exception inner = new RequestTimeoutException(error, lsn, partitionKeyRangeId, responseHeaders);
cause = new GoneException(resourceAddress, error, lsn, partitionKeyRangeId, responseHeaders, inner);
break;
case StatusCodes.RETRY_WITH:
cause = new RetryWithException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.SERVICE_UNAVAILABLE:
cause = new ServiceUnavailableException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.TOO_MANY_REQUESTS:
cause = new RequestRateTooLargeException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
case StatusCodes.UNAUTHORIZED:
cause = new UnauthorizedException(error, lsn, partitionKeyRangeId, responseHeaders);
break;
default:
cause = BridgeInternal.createCosmosException(resourceAddress, status.code(), error, responseHeaders);
break;
}
BridgeInternal.setResourceAddress(cause, resourceAddress);
requestRecord.completeExceptionally(cause);
}
}