@Override
  void handleResponse(ResponseInfo responseInfo, GetResponse getResponse) {
    if (isOperationComplete() || operationTracker.isDone()) {
      // If the successTarget is more than 1, then, different responses will have to be reconciled in some way. Here is where that
      // would be done. Since the store is immutable, currently we handle this by ignoring subsequent responses.
      return;
    }
    int correlationId = responseInfo.getRequestInfo().getRequest().getCorrelationId();
    // Get the GetOperation that generated the request.
    RequestInfo getRequestInfo = correlationIdToGetRequestInfo.remove(correlationId);
    if (getRequestInfo == null) {
      // Ignore. The request must have timed out.
      return;
    }
    if (responseInfo.isQuotaRejected()) {
      processQuotaRejectedResponse(correlationId, getRequestInfo.getReplicaId());
      return;
    }
    // Track the over all time taken for the response since the creation of the request.
    long requestLatencyMs = time.milliseconds() - getRequestInfo.getRequestCreateTime();
    routerMetrics.routerRequestLatencyMs.update(requestLatencyMs);
    routerMetrics.getDataNodeBasedMetrics(
        getRequestInfo.getReplicaId().getDataNodeId()).getBlobInfoRequestLatencyMs.update(requestLatencyMs);
    if (responseInfo.getError() != null) {
      logger.trace("GetBlobInfoRequest with response correlationId {} timed out for replica {} ", correlationId,
          getRequestInfo.getReplicaId().getDataNodeId());
      onErrorResponse(getRequestInfo.getReplicaId(), new RouterException(
          "Operation timed out because of " + responseInfo.getError() + " at DataNode " + responseInfo.getDataNode(),
          RouterErrorCode.OperationTimedOut));
    } else {
      if (getResponse == null) {
        logger.trace("GetBlobInfoRequest with response correlationId {} received an unexpected error on response"
            + "deserialization from replica {} ", correlationId, getRequestInfo.getReplicaId().getDataNodeId());
        onErrorResponse(getRequestInfo.getReplicaId(),
            new RouterException("Response deserialization received an unexpected error",
                RouterErrorCode.UnexpectedInternalError));
      } else {
        if (getResponse.getCorrelationId() != correlationId) {
          // The NetworkClient associates a response with a request based on the fact that only one request is sent
          // out over a connection id, and the response received on a connection id must be for the latest request
          // sent over it. The check here ensures that is indeed the case. If not, log an error and fail this request.
          // There is no other way to handle it.
          routerMetrics.unknownReplicaResponseError.inc();
          logger.trace("GetBlobInfoRequest with response correlationId {} mismatch from response {} for replica {} ",
              correlationId, getResponse.getCorrelationId(), getRequestInfo.getReplicaId().getDataNodeId());
          onErrorResponse(getRequestInfo.getReplicaId(), new RouterException(
              "The correlation id in the GetResponse " + getResponse.getCorrelationId()
                  + "is not the same as the correlation id in the associated GetRequest: " + correlationId,
              RouterErrorCode.UnexpectedInternalError));
          // we do not notify the ResponseHandler responsible for failure detection as this is an unexpected error.
        } else {
          try {
            processGetBlobInfoResponse(getRequestInfo, getResponse);
          } catch (IOException | MessageFormatException e) {
            // This should really not happen. Again, we do not notify the ResponseHandler responsible for failure
            // detection.
            logger.trace(
                "GetBlobInfoRequest with response correlationId {} response deserialization failed for replica {} ",
                correlationId, getRequestInfo.getReplicaId().getDataNodeId());
            routerMetrics.responseDeserializationErrorCount.inc();
            onErrorResponse(getRequestInfo.getReplicaId(),
                new RouterException("Response deserialization received an unexpected error", e,
                    RouterErrorCode.UnexpectedInternalError));
          }
        }
      }
    }
    checkAndMaybeComplete();
  }
Handle the given {@link ResponseInfo} and update the status of the operation.
Based on the status of the response, this involves handling the body, notifying the operation tracker,
notifying the response handler and possibly setting the operationException.
@param responseInfo the {@link ResponseInfo} to be handled.
@param getResponse The {@link GetResponse} associated with this response.
ambry-router/src/main/java/com/github/ambry/router/GetBlobInfoOperation.java