/** {@inheritDoc} */
  @Override
  public boolean onDone(@Nullable GridCacheCommittedTxInfo<K, V> res, @Nullable Throwable err) {
    if (super.onDone(res, err)) {
      cctx.mvcc().removeFuture(this);

      if (log.isDebugEnabled())
        log.debug(
            "Completing check committed tx future for transaction [tx="
                + tx
                + ", res="
                + res
                + ", err="
                + err
                + ']');

      if (err == null) cctx.tm().finishPessimisticTxOnRecovery(tx, res);
      else {
        if (log.isDebugEnabled())
          log.debug(
              "Failed to check prepared transactions, "
                  + "invalidating transaction [err="
                  + err
                  + ", tx="
                  + tx
                  + ']');

        if (nearCheck) return true;

        cctx.tm().salvageTx(tx);
      }

      return true;
    }

    return false;
  }
  /** Initializes future. */
  public void prepare() {
    if (log.isDebugEnabled())
      log.debug("Checking if transaction was committed on remote nodes: " + tx);

    // Check local node first (local node can be a backup node for some part of this transaction).
    long originatingThreadId = tx.threadId();

    if (tx instanceof GridCacheTxRemoteEx)
      originatingThreadId = ((GridCacheTxRemoteEx) tx).remoteThreadId();

    GridCacheCommittedTxInfo<K, V> txInfo =
        cctx.tm().txCommitted(tx.nearXidVersion(), tx.eventNodeId(), originatingThreadId);

    if (txInfo != null) {
      onDone(txInfo);

      markInitialized();

      return;
    }

    Collection<GridNode> checkNodes = CU.remoteNodes(cctx, tx.topologyVersion());

    if (tx instanceof GridDhtTxRemote) {
      // If we got primary node failure and near node has not failed.
      if (tx.nodeId().equals(failedNodeId) && !tx.eventNodeId().equals(failedNodeId)) {
        nearCheck = true;

        GridNode nearNode = cctx.discovery().node(tx.eventNodeId());

        if (nearNode == null) {
          // Near node failed, separate check prepared future will take care of it.
          onDone(
              new GridTopologyException(
                  "Failed to check near transaction state (near node left grid): "
                      + tx.eventNodeId()));

          return;
        }

        checkNodes = Collections.singletonList(nearNode);
      }
    }

    for (GridNode rmtNode : checkNodes) {
      // Skip left nodes and local node.
      if (rmtNode.id().equals(failedNodeId)) continue;

      /*
       * Send message to all cache nodes in the topology.
       */

      MiniFuture fut = new MiniFuture(rmtNode.id());

      GridCachePessimisticCheckCommittedTxRequest<K, V> req =
          new GridCachePessimisticCheckCommittedTxRequest<>(
              tx, originatingThreadId, futureId(), fut.futureId());

      add(fut);

      try {
        cctx.io().send(rmtNode.id(), req);
      } catch (GridTopologyException ignored) {
        fut.onNodeLeft();
      } catch (GridException e) {
        fut.onError(e);

        break;
      }
    }

    markInitialized();
  }