private void startScan(OpenScannerResponse resp) {
    addListener(
      conn.callerFactory.scanSingleRegion().id(resp.resp.getScannerId()).location(resp.loc)
        .remote(resp.isRegionServerRemote)
        .scannerLeaseTimeoutPeriod(resp.resp.getTtl(), TimeUnit.MILLISECONDS).stub(resp.stub)
        .setScan(scan).metrics(scanMetrics).consumer(consumer).resultCache(resultCache)
        .rpcTimeout(rpcTimeoutNs, TimeUnit.NANOSECONDS)
        .scanTimeout(scanTimeoutNs, TimeUnit.NANOSECONDS).pause(pauseNs, TimeUnit.NANOSECONDS)
        .pauseForServerOverloaded(pauseNsForServerOverloaded, TimeUnit.NANOSECONDS)
        .maxAttempts(maxAttempts).startLogErrorsCnt(startLogErrorsCnt)
        .setRequestAttributes(requestAttributes).start(resp.controller, resp.resp),
      (hasMore, error) -> {
        try (Scope ignored = span.makeCurrent()) {
          if (error != null) {
            try {
              consumer.onError(error);
              return;
            } finally {
              TraceUtil.setError(span, error);
              span.end();
            }
          }
          if (hasMore) {
            openScanner();
          } else {
            try {
              consumer.onComplete();
            } finally {
              span.setStatus(StatusCode.OK);
              span.end();
            }
          }
        }
      });
  }
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