void retry(final @Nullable Throwable failure) {
      if (observer.isTimedOut()) {
        // we running as a last attempt after a timeout has happened. don't retry
        Throwable listenFailure = failure;
        if (listenFailure == null) {
          if (shardIt == null) {
            listenFailure =
                new UnavailableShardsException(
                    new ShardId(request.concreteIndex(), -1),
                    "Timeout waiting for [{}], request: {}",
                    request.timeout(),
                    actionName);
          } else {
            listenFailure =
                new UnavailableShardsException(
                    shardIt.shardId(),
                    "[{}] shardIt, [{}] active : Timeout waiting for [{}], request: {}",
                    shardIt.size(),
                    shardIt.sizeActive(),
                    request.timeout(),
                    actionName);
          }
        }
        listener.onFailure(listenFailure);
        return;
      }

      observer.waitForNextChange(
          new ClusterStateObserver.Listener() {
            @Override
            public void onNewClusterState(ClusterState state) {
              doStart();
            }

            @Override
            public void onClusterServiceClose() {
              listener.onFailure(new NodeClosedException(nodes.localNode()));
            }

            @Override
            public void onTimeout(TimeValue timeout) {
              // just to be on the safe side, see if we can start it now?
              doStart();
            }
          },
          request.timeout());
    }
    protected void doStart() {
      nodes = observer.observedState().nodes();
      try {
        ClusterBlockException blockException = checkGlobalBlock(observer.observedState());
        if (blockException != null) {
          if (blockException.retryable()) {
            retry(blockException);
            return;
          } else {
            throw blockException;
          }
        }
        request.concreteIndex(
            indexNameExpressionResolver.concreteSingleIndex(observer.observedState(), request));
        // check if we need to execute, and if not, return
        if (!resolveRequest(observer.observedState(), request, listener)) {
          listener.onFailure(
              new IllegalStateException(
                  LoggerMessageFormat.format(
                      "{} request {} could not be resolved",
                      new ShardId(request.index, request.shardId),
                      actionName)));
          return;
        }
        blockException = checkRequestBlock(observer.observedState(), request);
        if (blockException != null) {
          if (blockException.retryable()) {
            retry(blockException);
            return;
          } else {
            throw blockException;
          }
        }
        shardIt = shards(observer.observedState(), request);
      } catch (Throwable e) {
        listener.onFailure(e);
        return;
      }

      // no shardIt, might be in the case between index gateway recovery and shardIt initialization
      if (shardIt.size() == 0) {
        retry(null);
        return;
      }

      // this transport only make sense with an iterator that returns a single shard routing (like
      // primary)
      assert shardIt.size() == 1;

      ShardRouting shard = shardIt.nextOrNull();
      assert shard != null;

      if (!shard.active()) {
        retry(null);
        return;
      }

      request.shardId = shardIt.shardId().id();
      DiscoveryNode node = nodes.get(shard.currentNodeId());
      transportService.sendRequest(
          node,
          shardActionName,
          request,
          transportOptions(),
          new BaseTransportResponseHandler<Response>() {

            @Override
            public Response newInstance() {
              return newResponse();
            }

            @Override
            public String executor() {
              return ThreadPool.Names.SAME;
            }

            @Override
            public void handleResponse(Response response) {
              listener.onResponse(response);
            }

            @Override
            public void handleException(TransportException exp) {
              Throwable cause = exp.unwrapCause();
              // if we got disconnected from the node, or the node / shard is not in the right state
              // (being closed)
              if (cause instanceof ConnectTransportException
                  || cause instanceof NodeClosedException
                  || retryOnFailure(exp)) {
                retry(cause);
              } else {
                listener.onFailure(exp);
              }
            }
          });
    }
 protected ClusterBlockException checkRequestBlock(ClusterState state, Request request) {
   return state.blocks().indexBlockedException(ClusterBlockLevel.WRITE, request.concreteIndex());
 }