private ResultScanner<Row> streamRows(ReadRowsRequest request) {
    final Timer.Context timerContext = readRowsAsync.getRpcMetrics().timeRpc();
    final AtomicBoolean wasCanceled = new AtomicBoolean(false);

    expandPoolIfNecessary(this.bigtableOptions.getChannelCount());

    CallOptions callOptions = getCallOptions(readRowsAsync.getMethodDescriptor(), request);
    final ClientCall<ReadRowsRequest, ReadRowsResponse> readRowsCall =
        readRowsAsync.newCall(callOptions);

    ResponseQueueReader reader =
        new ResponseQueueReader(
            retryOptions.getReadPartialRowTimeoutMillis(), retryOptions.getStreamingBufferSize());

    final StreamObserverAdapter<ReadRowsResponse> listener =
        new StreamObserverAdapter<>(readRowsCall, new RowMerger(reader));

    readRowsAsync.start(readRowsCall, request, listener, createMetadata(request.getTableName()));
    // If the scanner is closed before we're done streaming, we want to cancel the RPC.
    CancellationToken cancellationToken = new CancellationToken();
    cancellationToken.addListener(
        new Runnable() {
          @Override
          public synchronized void run() {
            if (!wasCanceled.get()) {
              timerContext.close();
              wasCanceled.set(true);
            }
            if (!listener.hasStatusBeenRecieved()) {
              readRowsCall.cancel("User requested cancelation.", null);
            }
          }
        },
        MoreExecutors.directExecutor());

    return new StreamingBigtableResultScanner(reader, cancellationToken);
  }
 private <ReqT, RespT> RetryingCollectingClientCallListener<ReqT, RespT> createStreamingListener(
     ReqT request, BigtableAsyncRpc<ReqT, RespT> rpc, String tableName) {
   CallOptions callOptions = getCallOptions(rpc.getMethodDescriptor(), request);
   return new RetryingCollectingClientCallListener<>(
       retryOptions, request, rpc, callOptions, retryExecutorService, createMetadata(tableName));
 }