protected AbstractSearchAsyncAction(
     Logger logger,
     SearchTransportService searchTransportService,
     Function<String, DiscoveryNode> nodeIdToDiscoveryNode,
     Map<String, AliasFilter> aliasFilter,
     Executor executor,
     SearchRequest request,
     ActionListener<SearchResponse> listener,
     GroupShardsIterator shardsIts,
     long startTime,
     long clusterStateVersion,
     SearchTask task) {
   super(startTime);
   this.logger = logger;
   this.searchTransportService = searchTransportService;
   this.executor = executor;
   this.request = request;
   this.task = task;
   this.listener = listener;
   this.nodeIdToDiscoveryNode = nodeIdToDiscoveryNode;
   this.clusterStateVersion = clusterStateVersion;
   this.shardsIts = shardsIts;
   expectedSuccessfulOps = shardsIts.size();
   // we need to add 1 for non active partition, since we count it in the total!
   expectedTotalOps = shardsIts.totalSizeWith1ForEmpty();
   firstResults = new AtomicArray<>(shardsIts.size());
   this.aliasFilter = aliasFilter;
 }
 public void start() {
   if (shardsIts.size() == 0) {
     // no shards
     try {
       listener.onResponse(newResponse(request, new AtomicReferenceArray(0), clusterState));
     } catch (Throwable e) {
       listener.onFailure(e);
     }
     return;
   }
   request.beforeStart();
   // count the local operations, and perform the non local ones
   int shardIndex = -1;
   for (final ShardIterator shardIt : shardsIts) {
     shardIndex++;
     final ShardRouting shard = shardIt.nextOrNull();
     if (shard != null) {
       performOperation(shardIt, shard, shardIndex);
     } else {
       // really, no shards active in this group
       onOperation(
           null, shardIt, shardIndex, new NoShardAvailableActionException(shardIt.shardId()));
     }
   }
 }
    protected AsyncBroadcastAction(Request request, ActionListener<Response> listener) {
      this.request = request;
      this.listener = listener;

      clusterState = clusterService.state();

      ClusterBlockException blockException = checkGlobalBlock(clusterState, request);
      if (blockException != null) {
        throw blockException;
      }
      // update to concrete indices
      String[] concreteIndices =
          clusterState.metaData().concreteIndices(request.indicesOptions(), request.indices());
      blockException = checkRequestBlock(clusterState, request, concreteIndices);
      if (blockException != null) {
        throw blockException;
      }

      nodes = clusterState.nodes();
      logger.trace("resolving shards based on cluster state version [{}]", clusterState.version());
      shardsIts = shards(clusterState, request, concreteIndices);
      expectedOps = shardsIts.size();

      shardsResponses = new AtomicReferenceArray<Object>(expectedOps);
    }
  protected final void addShardFailure(
      final int shardIndex, @Nullable SearchShardTarget shardTarget, Exception e) {
    // we don't aggregate shard failures on non active shards (but do keep the header counts right)
    if (TransportActions.isShardNotAvailableException(e)) {
      return;
    }

    // lazily create shard failures, so we can early build the empty shard failure list in most
    // cases (no failures)
    if (shardFailures == null) {
      synchronized (shardFailuresMutex) {
        if (shardFailures == null) {
          shardFailures = new AtomicArray<>(shardsIts.size());
        }
      }
    }
    ShardSearchFailure failure = shardFailures.get(shardIndex);
    if (failure == null) {
      shardFailures.set(shardIndex, new ShardSearchFailure(e, shardTarget));
    } else {
      // the failure is already present, try and not override it with an exception that is less
      // meaningless
      // for example, getting illegal shard state
      if (TransportActions.isReadOverrideException(e)) {
        shardFailures.set(shardIndex, new ShardSearchFailure(e, shardTarget));
      }
    }
  }
  void performFirstPhase(
      final int shardIndex, final ShardIterator shardIt, final ShardRouting shard) {
    if (shard == null) {
      // no more active shards... (we should not really get here, but just for safety)
      onFirstPhaseResult(
          shardIndex, null, null, shardIt, new NoShardAvailableActionException(shardIt.shardId()));
    } else {
      final DiscoveryNode node = nodeIdToDiscoveryNode.apply(shard.currentNodeId());
      if (node == null) {
        onFirstPhaseResult(
            shardIndex,
            shard,
            null,
            shardIt,
            new NoShardAvailableActionException(shardIt.shardId()));
      } else {
        AliasFilter filter = this.aliasFilter.get(shard.index().getUUID());
        assert filter != null;
        ShardSearchTransportRequest transportRequest =
            new ShardSearchTransportRequest(
                request, shardIt.shardId(), shardsIts.size(), filter, startTime());
        sendExecuteFirstPhase(
            node,
            transportRequest,
            new ActionListener<FirstResult>() {
              @Override
              public void onResponse(FirstResult result) {
                onFirstPhaseResult(shardIndex, shard.currentNodeId(), result, shardIt);
              }

              @Override
              public void onFailure(Exception t) {
                onFirstPhaseResult(shardIndex, shard, node.getId(), shardIt, t);
              }
            });
      }
    }
  }
 public void start() {
   if (shardsIts.size() == 0) {
     // no shards
     try {
       listener.onResponse(newResponse(request, new AtomicReferenceArray(0), clusterState));
     } catch (Throwable e) {
       listener.onFailure(e);
     }
   }
   request.beforeStart();
   // count the local operations, and perform the non local ones
   int localOperations = 0;
   int shardIndex = -1;
   for (final ShardIterator shardIt : shardsIts) {
     shardIndex++;
     final ShardRouting shard = shardIt.firstOrNull();
     if (shard != null) {
       if (shard.currentNodeId().equals(nodes.localNodeId())) {
         localOperations++;
       } else {
         // do the remote operation here, the localAsync flag is not relevant
         performOperation(shardIt, shardIndex, true);
       }
     } else {
       // really, no shards active in this group
       onOperation(
           null, shardIt, shardIndex, new NoShardAvailableActionException(shardIt.shardId()));
     }
   }
   // we have local operations, perform them now
   if (localOperations > 0) {
     if (request.operationThreading() == BroadcastOperationThreading.SINGLE_THREAD) {
       request.beforeLocalFork();
       threadPool
           .executor(executor)
           .execute(
               new Runnable() {
                 @Override
                 public void run() {
                   int shardIndex = -1;
                   for (final ShardIterator shardIt : shardsIts) {
                     shardIndex++;
                     final ShardRouting shard = shardIt.firstOrNull();
                     if (shard != null) {
                       if (shard.currentNodeId().equals(nodes.localNodeId())) {
                         performOperation(shardIt, shardIndex, false);
                       }
                     }
                   }
                 }
               });
     } else {
       boolean localAsync =
           request.operationThreading() == BroadcastOperationThreading.THREAD_PER_SHARD;
       if (localAsync) {
         request.beforeLocalFork();
       }
       shardIndex = -1;
       for (final ShardIterator shardIt : shardsIts) {
         shardIndex++;
         final ShardRouting shard = shardIt.firstOrNull();
         if (shard != null) {
           if (shard.currentNodeId().equals(nodes.localNodeId())) {
             performOperation(shardIt, shardIndex, localAsync);
           }
         }
       }
     }
   }
 }
 private int numShards(String... index) {
   ClusterState state = client().admin().cluster().prepareState().get().getState();
   GroupShardsIterator shardIterators =
       state.getRoutingTable().activePrimaryShardsGrouped(index, false);
   return shardIterators.size();
 }
  @Override
  protected void doExecute(final Request request, final ActionListener<Response> listener) {
    ClusterState clusterState = clusterService.state();
    ClusterBlockException blockException = checkGlobalBlock(clusterState, request);
    if (blockException != null) {
      throw blockException;
    }
    // update to concrete index
    request.index(
        clusterState.metaData().concreteSingleIndex(request.index(), request.indicesOptions()));
    blockException = checkRequestBlock(clusterState, request);
    if (blockException != null) {
      throw blockException;
    }

    GroupShardsIterator groups;
    try {
      groups = shards(request);
    } catch (Throwable e) {
      listener.onFailure(e);
      return;
    }
    final AtomicInteger indexCounter = new AtomicInteger();
    final AtomicInteger failureCounter = new AtomicInteger();
    final AtomicInteger completionCounter = new AtomicInteger(groups.size());
    final AtomicReferenceArray<ShardActionResult> shardsResponses =
        new AtomicReferenceArray<>(groups.size());

    for (final ShardIterator shardIt : groups) {
      ShardRequest shardRequest = newShardRequestInstance(request, shardIt.shardId().id());

      // TODO for now, we fork operations on shardIt of the index
      shardRequest.beforeLocalFork(); // optimize for local fork
      shardRequest.operationThreaded(true);

      // no need for threaded listener, we will fork when its done based on the index request
      shardRequest.listenerThreaded(false);
      shardAction.execute(
          shardRequest,
          new ActionListener<ShardResponse>() {
            @Override
            public void onResponse(ShardResponse result) {
              shardsResponses.set(indexCounter.getAndIncrement(), new ShardActionResult(result));
              returnIfNeeded();
            }

            @Override
            public void onFailure(Throwable e) {
              failureCounter.getAndIncrement();
              int index = indexCounter.getAndIncrement();
              if (accumulateExceptions()) {
                shardsResponses.set(
                    index,
                    new ShardActionResult(
                        new DefaultShardOperationFailedException(
                            request.index, shardIt.shardId().id(), e)));
              }
              returnIfNeeded();
            }

            private void returnIfNeeded() {
              if (completionCounter.decrementAndGet() == 0) {
                List<ShardResponse> responses = Lists.newArrayList();
                List<ShardOperationFailedException> failures = Lists.newArrayList();
                for (int i = 0; i < shardsResponses.length(); i++) {
                  ShardActionResult shardActionResult = shardsResponses.get(i);
                  if (shardActionResult == null) {
                    assert !accumulateExceptions();
                    continue;
                  }
                  if (shardActionResult.isFailure()) {
                    assert accumulateExceptions() && shardActionResult.shardFailure != null;
                    failures.add(shardActionResult.shardFailure);
                  } else {
                    responses.add(shardActionResult.shardResponse);
                  }
                }

                assert failures.size() == 0 || failures.size() == failureCounter.get();
                listener.onResponse(
                    newResponseInstance(request, responses, failureCounter.get(), failures));
              }
            }
          });
    }
  }
    ASyncAction(
        MultiPercolateRequest multiPercolateRequest,
        List<Object> percolateRequests,
        ActionListener<MultiPercolateResponse> finalListener,
        ClusterState clusterState) {
      this.finalListener = finalListener;
      this.multiPercolateRequest = multiPercolateRequest;
      this.percolateRequests = percolateRequests;
      responsesByItemAndShard = new AtomicReferenceArray<>(percolateRequests.size());
      expectedOperationsPerItem = new AtomicReferenceArray<>(percolateRequests.size());
      reducedResponses = new AtomicArray<>(percolateRequests.size());

      // Resolving concrete indices and routing and grouping the requests by shard
      requestsByShard = new HashMap<>();
      // Keep track what slots belong to what shard, in case a request to a shard fails on all
      // copies
      shardToSlots = new HashMap<>();
      int expectedResults = 0;
      for (int slot = 0; slot < percolateRequests.size(); slot++) {
        Object element = percolateRequests.get(slot);
        assert element != null;
        if (element instanceof PercolateRequest) {
          PercolateRequest percolateRequest = (PercolateRequest) element;
          String[] concreteIndices;
          try {
            concreteIndices =
                indexNameExpressionResolver.concreteIndices(clusterState, percolateRequest);
          } catch (IndexNotFoundException e) {
            reducedResponses.set(slot, e);
            responsesByItemAndShard.set(slot, new AtomicReferenceArray(0));
            expectedOperationsPerItem.set(slot, new AtomicInteger(0));
            continue;
          }
          Map<String, Set<String>> routing =
              indexNameExpressionResolver.resolveSearchRouting(
                  clusterState, percolateRequest.routing(), percolateRequest.indices());
          // TODO: I only need shardIds, ShardIterator(ShardRouting) is only needed in
          // TransportShardMultiPercolateAction
          GroupShardsIterator shards =
              clusterService
                  .operationRouting()
                  .searchShards(
                      clusterState, concreteIndices, routing, percolateRequest.preference());
          if (shards.size() == 0) {
            reducedResponses.set(slot, new UnavailableShardsException(null, "No shards available"));
            responsesByItemAndShard.set(slot, new AtomicReferenceArray(0));
            expectedOperationsPerItem.set(slot, new AtomicInteger(0));
            continue;
          }

          // The shard id is used as index in the atomic ref array, so we need to find out how many
          // shards there are regardless of routing:
          int numShards =
              clusterService
                  .operationRouting()
                  .searchShardsCount(clusterState, concreteIndices, null);
          responsesByItemAndShard.set(slot, new AtomicReferenceArray(numShards));
          expectedOperationsPerItem.set(slot, new AtomicInteger(shards.size()));
          for (ShardIterator shard : shards) {
            ShardId shardId = shard.shardId();
            TransportShardMultiPercolateAction.Request requests = requestsByShard.get(shardId);
            if (requests == null) {
              requestsByShard.put(
                  shardId,
                  requests =
                      new TransportShardMultiPercolateAction.Request(
                          multiPercolateRequest,
                          shardId.getIndex(),
                          shardId.getId(),
                          percolateRequest.preference()));
            }
            logger.trace("Adding shard[{}] percolate request for item[{}]", shardId, slot);
            requests.add(
                new TransportShardMultiPercolateAction.Request.Item(
                    slot, new PercolateShardRequest(shardId, percolateRequest)));

            IntArrayList items = shardToSlots.get(shardId);
            if (items == null) {
              shardToSlots.put(shardId, items = new IntArrayList());
            }
            items.add(slot);
          }
          expectedResults++;
        } else if (element instanceof Throwable || element instanceof MultiGetResponse.Failure) {
          logger.trace("item[{}] won't be executed, reason: {}", slot, element);
          reducedResponses.set(slot, element);
          responsesByItemAndShard.set(slot, new AtomicReferenceArray(0));
          expectedOperationsPerItem.set(slot, new AtomicInteger(0));
        }
      }
      expectedOperations = new AtomicInteger(expectedResults);
    }