private CompletableFuture<OpenScannerResponse> callOpenScanner(HBaseRpcController controller,
    HRegionLocation loc, ClientService.Interface stub) {
    try (Scope ignored = span.makeCurrent()) {
      boolean isRegionServerRemote = isRemote(loc.getHostname());
      incRPCCallsMetrics(scanMetrics, isRegionServerRemote);
      if (openScannerTries.getAndIncrement() > 1) {
        incRPCRetriesMetrics(scanMetrics, isRegionServerRemote);
      }
      CompletableFuture<OpenScannerResponse> future = new CompletableFuture<>();
      try {
        ScanRequest request = RequestConverter.buildScanRequest(loc.getRegion().getRegionName(),
          scan, scan.getCaching(), false);
        stub.scan(controller, request, resp -> {
          try (Scope ignored1 = span.makeCurrent()) {
            if (controller.failed()) {
              final IOException e = controller.getFailed();
              future.completeExceptionally(e);
              TraceUtil.setError(span, e);
              span.end();
              return;
            }
            future
              .complete(new OpenScannerResponse(loc, isRegionServerRemote, stub, controller, resp));
          }
        });
      } catch (IOException e) {
        // span is closed by listener attached to the Future in `openScanner()`
        future.completeExceptionally(e);
      }
      return future;
    }
  }
The asynchronous client scanner implementation.
<p>
Here we will call OpenScanner first and use the returned scannerId to create a
{@link AsyncScanSingleRegionRpcRetryingCaller} to do the real scan operation. The return value of
{@link AsyncScanSingleRegionRpcRetryingCaller} will tell us whether open a new scanner or finish
scan.
hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncClientScanner.java