private void innerFinishHim() {
   InternalSearchResponse internalResponse =
       searchPhaseController.merge(sortedShardList, queryResults, fetchResults);
   String scrollId = null;
   if (request.scroll() != null) {
     scrollId = request.scrollId();
   }
   listener.onResponse(
       new SearchResponse(
           internalResponse,
           scrollId,
           this.scrollId.getContext().length,
           successfulOps.get(),
           System.currentTimeMillis() - startTime,
           buildShardFailures()));
 }
  @Test
  public void testParseSearchScrollRequest() throws Exception {
    BytesReference content =
        XContentFactory.jsonBuilder()
            .startObject()
            .field("scroll_id", "SCROLL_ID")
            .field("scroll", "1m")
            .endObject()
            .bytes();

    SearchScrollRequest searchScrollRequest = new SearchScrollRequest();
    RestSearchScrollAction.buildFromContent(content, searchScrollRequest);

    assertThat(searchScrollRequest.scrollId(), equalTo("SCROLL_ID"));
    assertThat(
        searchScrollRequest.scroll().keepAlive(),
        equalTo(TimeValue.parseTimeValue("1m", null, "scroll")));
  }
    public void start() {
      if (scrollId.getContext().length == 0) {
        listener.onFailure(
            new SearchPhaseExecutionException("query", "no nodes to search on", null));
        return;
      }
      final AtomicInteger counter = new AtomicInteger(scrollId.getContext().length);

      int localOperations = 0;
      for (Tuple<String, Long> target : scrollId.getContext()) {
        DiscoveryNode node = nodes.get(target.v1());
        if (node != null) {
          if (nodes.localNodeId().equals(node.id())) {
            localOperations++;
          } else {
            executeQueryPhase(counter, node, target.v2());
          }
        } else {
          if (logger.isDebugEnabled()) {
            logger.debug(
                "Node ["
                    + target.v1()
                    + "] not available for scroll request ["
                    + scrollId.getSource()
                    + "]");
          }
          successfulOps.decrementAndGet();
          if (counter.decrementAndGet() == 0) {
            executeFetchPhase();
          }
        }
      }

      if (localOperations > 0) {
        if (request.getOperationThreading() == SearchOperationThreading.SINGLE_THREAD) {
          threadPool
              .executor(ThreadPool.Names.SEARCH)
              .execute(
                  new Runnable() {
                    @Override
                    public void run() {
                      for (Tuple<String, Long> target : scrollId.getContext()) {
                        DiscoveryNode node = nodes.get(target.v1());
                        if (node != null && nodes.localNodeId().equals(node.id())) {
                          executeQueryPhase(counter, node, target.v2());
                        }
                      }
                    }
                  });
        } else {
          boolean localAsync =
              request.getOperationThreading() == SearchOperationThreading.THREAD_PER_SHARD;
          for (final Tuple<String, Long> target : scrollId.getContext()) {
            final DiscoveryNode node = nodes.get(target.v1());
            if (node != null && nodes.localNodeId().equals(node.id())) {
              if (localAsync) {
                threadPool
                    .executor(ThreadPool.Names.SEARCH)
                    .execute(
                        new Runnable() {
                          @Override
                          public void run() {
                            executeQueryPhase(counter, node, target.v2());
                          }
                        });
              } else {
                executeQueryPhase(counter, node, target.v2());
              }
            }
          }
        }
      }
    }