/** {@inheritDoc} */
  @Override
  public boolean tmLock(GridCacheTxEx<K, V> tx, long timeout)
      throws GridCacheEntryRemovedException, GridDistributedLockCancelledException {
    if (tx.local()) {
      GridDhtTxLocal<K, V> dhtTx = (GridDhtTxLocal<K, V>) tx;

      // Null is returned if timeout is negative and there is other lock owner.
      return addDhtLocal(
              dhtTx.nearNodeId(),
              dhtTx.nearXidVersion(),
              tx.threadId(),
              tx.xidVersion(),
              timeout,
              false,
              tx.ec(),
              true)
          != null;
    }

    try {
      addRemote(
          tx.nodeId(),
          tx.otherNodeId(),
          tx.threadId(),
          tx.xidVersion(),
          tx.timeout(),
          tx.ec(),
          true);

      return true;
    } catch (GridDistributedLockCancelledException ignored) {
      if (log.isDebugEnabled())
        log.debug("Attempted to enter tx lock for cancelled ID (will ignore): " + tx);

      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();
  }