@Override
  public Pair<Message, ExtendedCellScanner> call(RpcCall call, MonitoredRPCHandler status)
    throws IOException {
    try {
      MethodDescriptor md = call.getMethod();
      Message param = call.getParam();
      status.setRPC(md.getName(), new Object[] { param }, call.getReceiveTime());
      // TODO: Review after we add in encoded data blocks.
      status.setRPCPacket(param);
      status.resume("Servicing call");
      // get an instance of the method arg type
      HBaseRpcController controller = new HBaseRpcControllerImpl(call.getCellScanner());
      controller.setCallTimeout(call.getTimeout());
      Message result = call.getService().callBlockingMethod(md, controller, param);
      long receiveTime = call.getReceiveTime();
      long startTime = call.getStartTime();
      long endTime = EnvironmentEdgeManager.currentTime();
      int processingTime = (int) (endTime - startTime);
      int qTime = (int) (startTime - receiveTime);
      int totalTime = (int) (endTime - receiveTime);
      if (LOG.isTraceEnabled()) {
        LOG.trace(
          "{}, response: {}, receiveTime: {}, queueTime: {}, processingTime: {}, "
            + "totalTime: {}, fsReadTime: {}",
          CurCall.get().toString(), TextFormat.shortDebugString(result),
          CurCall.get().getReceiveTime(), qTime, processingTime, totalTime,
          CurCall.get().getFsReadTime());
      }
      // Use the raw request call size for now.
      long requestSize = call.getSize();
      long responseSize = result.getSerializedSize();
      long responseBlockSize = call.getBlockBytesScanned();
      long fsReadTime = call.getFsReadTime();
      if (call.isClientCellBlockSupported()) {
        // Include the payload size in HBaseRpcController
        responseSize += call.getResponseCellSize();
      }

      metrics.dequeuedCall(qTime);
      metrics.processedCall(processingTime);
      metrics.totalCall(totalTime);
      metrics.receivedRequest(requestSize);
      metrics.sentResponse(responseSize);
      // log any RPC responses that are slower than the configured warn
      // response time or larger than configured warning size
      boolean tooSlow = isTooSlow(call, processingTime);
      boolean tooLarge = isTooLarge(call, responseSize, responseBlockSize);
      if (tooSlow || tooLarge) {
        final String userName = call.getRequestUserName().orElse(StringUtils.EMPTY);
        // when tagging, we let TooLarge trump TooSmall to keep output simple
        // note that large responses will often also be slow.
        logResponse(param, md.getName(), md.getName() + "(" + param.getClass().getName() + ")",
          tooLarge, tooSlow, status.getClient(), startTime, processingTime, qTime, responseSize,
          responseBlockSize, fsReadTime, userName);
        if (this.namedQueueRecorder != null && this.isOnlineLogProviderEnabled) {
          // send logs to ring buffer owned by slowLogRecorder
          final String className =
            server == null ? StringUtils.EMPTY : server.getClass().getSimpleName();
          this.namedQueueRecorder.addRecord(new RpcLogDetails(call, param, status.getClient(),
            responseSize, responseBlockSize, fsReadTime, className, tooSlow, tooLarge));
        }
      }
      return new Pair<>(result, controller.cellScanner());
    } catch (Throwable e) {
      // The above callBlockingMethod will always return a SE. Strip the SE wrapper before
      // putting it on the wire. Its needed to adhere to the pb Service Interface but we don't
      // need to pass it over the wire.
      if (e instanceof ServiceException) {
        if (e.getCause() == null) {
          LOG.debug("Caught a ServiceException with null cause", e);
        } else {
          e = e.getCause();
        }
      }

      // increment the number of requests that were exceptions.
      metrics.exception(e);

      if (e instanceof LinkageError) throw new DoNotRetryIOException(e);
      if (e instanceof IOException) throw (IOException) e;
      LOG.error("Unexpected throwable object ", e);
      throw new IOException(e.getMessage(), e);
    }
  }
This is a server side method, which is invoked over RPC. On success the return response has
protobuf response payload. On failure, the exception name and the stack trace are returned in
the protobuf response.
hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServer.java