/**
   * @param nodes Nodes.
   * @param msg Message.
   * @param partsMap Partitions.
   * @return {@code true} If all messages sent successfully.
   */
  private boolean send(
      Collection<ClusterNode> nodes, Message msg, Map<ClusterNode, IntArray> partsMap) {
    boolean locNodeFound = false;

    boolean ok = true;

    for (ClusterNode node : nodes) {
      if (node.isLocal()) {
        locNodeFound = true;

        continue;
      }

      try {
        ctx.io().send(node, GridTopic.TOPIC_QUERY, copy(msg, node, partsMap), QUERY_POOL);
      } catch (IgniteCheckedException e) {
        ok = false;

        U.warn(log, e.getMessage());
      }
    }

    if (locNodeFound) // Local node goes the last to allow parallel execution.
    h2.mapQueryExecutor()
          .onMessage(ctx.localNodeId(), copy(msg, ctx.discovery().localNode(), partsMap));

    return ok;
  }
  /**
   * Rolls back local part of colocated transaction.
   *
   * @return Commit future.
   */
  public IgniteInternalFuture<IgniteInternalTx> rollbackAsyncLocal() {
    if (log.isDebugEnabled()) log.debug("Rolling back colocated tx locally: " + this);

    final GridDhtTxFinishFuture fut = new GridDhtTxFinishFuture<>(cctx, this, /*commit*/ false);

    cctx.mvcc().addFuture(fut, fut.futureId());

    IgniteInternalFuture<?> prep = prepFut.get();

    if (prep == null || prep.isDone()) {
      try {
        if (prep != null) prep.get();
      } catch (IgniteCheckedException e) {
        if (log.isDebugEnabled())
          log.debug(
              "Failed to prepare transaction during rollback (will ignore) [tx="
                  + this
                  + ", msg="
                  + e.getMessage()
                  + ']');
      }

      fut.finish();
    } else
      prep.listen(
          new CI1<IgniteInternalFuture<?>>() {
            @Override
            public void apply(IgniteInternalFuture<?> f) {
              try {
                f.get(); // Check for errors of a parent future.
              } catch (IgniteCheckedException e) {
                log.debug(
                    "Failed to prepare transaction during rollback (will ignore) [tx="
                        + this
                        + ", msg="
                        + e.getMessage()
                        + ']');
              }

              fut.finish();
            }
          });

    return fut;
  }
  /**
   * Send delete message to all meta cache nodes in the grid.
   *
   * @param msg Message to send.
   */
  private void sendDeleteMessage(IgfsDeleteMessage msg) {
    assert msg != null;

    Collection<ClusterNode> nodes = meta.metaCacheNodes();

    for (ClusterNode node : nodes) {
      try {
        igfsCtx.send(node, topic, msg, GridIoPolicy.SYSTEM_POOL);
      } catch (IgniteCheckedException e) {
        U.warn(
            log,
            "Failed to send IGFS delete message to node [nodeId="
                + node.id()
                + ", msg="
                + msg
                + ", err="
                + e.getMessage()
                + ']');
      }
    }
  }