/** @return Nodes to execute on. */
  private Collection<GridNode> nodes() {
    GridCacheMode cacheMode = cctx.config().getCacheMode();

    switch (cacheMode) {
      case LOCAL:
        if (prj != null)
          U.warn(
              log,
              "Ignoring query projection because it's executed over LOCAL cache "
                  + "(only local node will be queried): "
                  + this);

        return Collections.singletonList(cctx.localNode());

      case REPLICATED:
        if (prj != null) return nodes(cctx, prj);

        GridCacheDistributionMode mode = cctx.config().getDistributionMode();

        return mode == PARTITIONED_ONLY || mode == NEAR_PARTITIONED
            ? Collections.singletonList(cctx.localNode())
            : Collections.singletonList(F.rand(nodes(cctx, null)));

      case PARTITIONED:
        return nodes(cctx, prj);

      default:
        throw new IllegalStateException("Unknown cache distribution mode: " + cacheMode);
    }
  }
示例#2
0
  /**
   * @return Collection of readers after check.
   * @throws GridCacheEntryRemovedException If removed.
   */
  public Collection<ReaderId> checkReaders() throws GridCacheEntryRemovedException {
    synchronized (mux) {
      checkObsolete();

      if (!readers.isEmpty()) {
        List<ReaderId> rmv = null;

        for (ReaderId reader : readers) {
          if (!cctx.discovery().alive(reader.nodeId())) {
            if (rmv == null) rmv = new LinkedList<ReaderId>();

            rmv.add(reader);
          }
        }

        if (rmv != null) {
          readers = new LinkedList<ReaderId>(readers);

          for (ReaderId rdr : rmv) readers.remove(rdr);

          readers = Collections.unmodifiableList(readers);
        }
      }

      return readers;
    }
  }
  /** {@inheritDoc} */
  @Override
  public GridFuture<Map<K, V>> getAllAsync(
      @Nullable Collection<? extends K> keys,
      @Nullable GridPredicate<? super GridCacheEntry<K, V>>[] filter) {
    ctx.denyOnFlag(LOCAL);

    if (F.isEmpty(keys))
      return new GridFinishedFuture<Map<K, V>>(ctx.kernalContext(), Collections.<K, V>emptyMap());

    GridCacheTxLocalAdapter<K, V> tx = ctx.tm().threadLocalTx();

    if (tx != null && !tx.implicit()) return ctx.wrapCloneMap(tx.getAllAsync(keys, filter));

    return loadAsync(keys, false, filter);
  }
  /**
   * @param keys Keys to load.
   * @param reload Reload flag.
   * @param filter Filter.
   * @return Loaded values.
   */
  public GridFuture<Map<K, V>> loadAsync(
      @Nullable Collection<? extends K> keys,
      boolean reload,
      @Nullable GridPredicate<? super GridCacheEntry<K, V>>[] filter) {
    if (F.isEmpty(keys))
      return new GridFinishedFuture<Map<K, V>>(ctx.kernalContext(), Collections.<K, V>emptyMap());

    GridNearGetFuture<K, V> fut = new GridNearGetFuture<K, V>(ctx, keys, reload, null, filter);

    // Register future for responses.
    ctx.mvcc().addFuture(fut);

    fut.init();

    return ctx.wrapCloneMap(fut);
  }
示例#5
0
  /**
   * @param nodeId Reader to remove.
   * @param msgId Message ID.
   * @return {@code True} if reader was removed as a result of this operation.
   * @throws GridCacheEntryRemovedException If entry was removed.
   */
  public boolean removeReader(UUID nodeId, long msgId) throws GridCacheEntryRemovedException {
    synchronized (mux) {
      checkObsolete();

      ReaderId reader = readerId(nodeId);

      if (reader == null || reader.messageId() > msgId) return false;

      readers = new LinkedList<ReaderId>(readers);

      readers.remove(reader);

      // Seal.
      readers = Collections.unmodifiableList(readers);

      return true;
    }
  }
  /** {@inheritDoc} */
  @Override
  public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    super.readExternal(in);

    futId = U.readGridUuid(in);
    miniId = U.readGridUuid(in);
    entries = U.readCollection(in);
    invalidParts = U.readIntSet(in);

    ver = CU.readVersion(in);

    err = (Throwable) in.readObject();

    if (invalidParts == null) invalidParts = Collections.emptyList();

    assert futId != null;
    assert miniId != null;
    assert ver != null;
  }
 /** @return Near keys. */
 public List<byte[]> nearKeyBytes() {
   return nearKeyBytes != null ? nearKeyBytes : Collections.<byte[]>emptyList();
 }
示例#8
0
 /** {@inheritDoc} */
 @Override
 public Map<K, Collection<GridCacheMvccCandidate<K>>> localCandidates() {
   return Collections.emptyMap();
 }
  /**
   * @param nodeId Primary node ID.
   * @param req Request.
   * @return Remote transaction.
   * @throws GridException If failed.
   * @throws GridDistributedLockCancelledException If lock has been cancelled.
   */
  @SuppressWarnings({"RedundantTypeArguments"})
  @Nullable
  public GridNearTxRemote<K, V> startRemoteTxForFinish(
      UUID nodeId, GridDhtTxFinishRequest<K, V> req)
      throws GridException, GridDistributedLockCancelledException {
    GridNearTxRemote<K, V> tx = null;

    ClassLoader ldr = ctx.deploy().globalLoader();

    if (ldr != null) {
      for (GridCacheTxEntry<K, V> txEntry : req.nearWrites()) {
        GridDistributedCacheEntry<K, V> entry = null;

        while (true) {
          try {
            entry = peekExx(txEntry.key());

            if (entry != null) {
              entry.keyBytes(txEntry.keyBytes());

              // Handle implicit locks for pessimistic transactions.
              tx = ctx.tm().tx(req.version());

              if (tx != null) {
                if (tx.local()) return null;

                if (tx.markFinalizing()) tx.addWrite(txEntry.key(), txEntry.keyBytes());
                else return null;
              } else {
                tx =
                    new GridNearTxRemote<K, V>(
                        nodeId,
                        req.nearNodeId(),
                        req.threadId(),
                        req.version(),
                        null,
                        PESSIMISTIC,
                        req.isolation(),
                        req.isInvalidate(),
                        0,
                        txEntry.key(),
                        txEntry.keyBytes(),
                        txEntry.value(),
                        txEntry.valueBytes(),
                        ctx);

                if (tx.empty()) return tx;

                tx = ctx.tm().onCreated(tx);

                if (tx == null || !ctx.tm().onStarted(tx))
                  throw new GridCacheTxRollbackException(
                      "Failed to acquire lock "
                          + "(transaction has been completed): "
                          + req.version());

                if (!tx.markFinalizing()) return null;
              }

              // Add remote candidate before reordering.
              if (txEntry.explicitVersion() == null)
                entry.addRemote(
                    req.nearNodeId(),
                    nodeId,
                    req.threadId(),
                    req.version(),
                    0,
                    tx.ec(),
                    /*tx*/ true,
                    tx.implicitSingle());

              // Remote candidates for ordered lock queuing.
              entry.addRemoteCandidates(
                  Collections.<GridCacheMvccCandidate<K>>emptyList(),
                  req.version(),
                  req.committedVersions(),
                  req.rolledbackVersions());
            }

            // Double-check in case if sender node left the grid.
            if (ctx.discovery().node(req.nearNodeId()) == null) {
              if (log.isDebugEnabled())
                log.debug("Node requesting lock left grid (lock request will be ignored): " + req);

              if (tx != null) tx.rollback();

              return null;
            }

            // Entry is legit.
            break;
          } catch (GridCacheEntryRemovedException ignored) {
            assert entry.obsoleteVersion() != null
                : "Obsolete flag not set on removed entry: " + entry;

            if (log.isDebugEnabled())
              log.debug("Received entry removed exception (will retry on renewed entry): " + entry);

            if (tx != null) {
              tx.clearEntry(entry.key());

              if (log.isDebugEnabled())
                log.debug(
                    "Cleared removed entry from remote transaction (will retry) [entry="
                        + entry
                        + ", tx="
                        + tx
                        + ']');
            }
          }
        }
      }
    } else {
      String err = "Failed to acquire deployment class loader for message: " + req;

      U.warn(log, err);

      throw new GridException(err);
    }

    return tx;
  }
示例#10
0
 /** @return Collection of recovery writes. */
 public Collection<GridCacheTxEntry<K, V>> recoveryWrites() {
   return recoveryWrites == null
       ? Collections.<GridCacheTxEntry<K, V>>emptyList()
       : recoveryWrites;
 }
示例#11
0
 /** {@inheritDoc} */
 @SuppressWarnings({"unchecked"})
 @Override
 public Map<GridRichNode, Collection<K>> mapKeysToNodes(Collection<? extends K> keys) {
   return Collections.singletonMap(ctx.localNode(), (Collection<K>) keys);
 }
  /** @throws Exception If failed. */
  public void testEmptyProjections() throws Exception {
    final GridClientCompute dflt = client.compute();

    Collection<? extends GridClientNode> nodes = dflt.nodes();

    assertEquals(NODES_CNT, nodes.size());

    Iterator<? extends GridClientNode> iter = nodes.iterator();

    final GridClientCompute singleNodePrj = dflt.projection(Collections.singletonList(iter.next()));

    final GridClientNode second = iter.next();

    final GridClientPredicate<GridClientNode> noneFilter =
        new GridClientPredicate<GridClientNode>() {
          @Override
          public boolean apply(GridClientNode node) {
            return false;
          }
        };

    final GridClientPredicate<GridClientNode> targetFilter =
        new GridClientPredicate<GridClientNode>() {
          @Override
          public boolean apply(GridClientNode node) {
            return node.nodeId().equals(second.nodeId());
          }
        };

    GridTestUtils.assertThrows(
        log(),
        new Callable<Object>() {
          @Override
          public Object call() throws Exception {
            return dflt.projection(noneFilter).log(-1, -1);
          }
        },
        GridServerUnreachableException.class,
        null);

    GridTestUtils.assertThrows(
        log(),
        new Callable<Object>() {
          @Override
          public Object call() throws Exception {
            return singleNodePrj.projection(second);
          }
        },
        GridClientException.class,
        null);

    GridTestUtils.assertThrows(
        log(),
        new Callable<Object>() {
          @Override
          public Object call() throws Exception {
            return singleNodePrj.projection(targetFilter);
          }
        },
        GridClientException.class,
        null);
  }
示例#13
0
 /** Clears all readers (usually when partition becomes invalid and ready for eviction). */
 @Override
 public void clearReaders() {
   synchronized (mux) {
     readers = Collections.emptyList();
   }
 }
示例#14
0
/**
 * Replicated cache entry.
 *
 * @author 2005-2011 Copyright (C) GridGain Systems, Inc.
 * @version 3.1.1c.19062011
 */
public class GridDhtCacheEntry<K, V> extends GridDistributedCacheEntry<K, V> {
  /** Gets node value from reader ID. */
  private static final GridClosure<ReaderId, UUID> R2N =
      new C1<ReaderId, UUID>() {
        @Override
        public UUID apply(ReaderId e) {
          return e.nodeId();
        }
      };

  /** Reader clients. */
  @GridToStringInclude private volatile List<ReaderId> readers = Collections.emptyList();

  /** Local partition. */
  private final GridDhtLocalPartition<K, V> locPart;

  /** Transactions future for added readers. */
  private volatile GridCacheMultiTxFuture<K, V> txFut;

  /**
   * @param ctx Cache context.
   * @param key Cache key.
   * @param hash Key hash value.
   * @param val Entry value.
   * @param next Next entry in the linked list.
   * @param ttl Time to live.
   */
  public GridDhtCacheEntry(
      GridCacheContext<K, V> ctx, K key, int hash, V val, GridCacheMapEntry<K, V> next, long ttl) {
    super(ctx, key, hash, val, next, ttl);

    // Record this entry with partition.
    locPart = ctx.dht().topology().onAdded(this);
  }

  /** {@inheritDoc} */
  @Override
  public boolean partitionValid() {
    return locPart.valid();
  }

  /** {@inheritDoc} */
  @Override
  public boolean markObsolete(GridCacheVersion ver) {
    boolean rmv;

    synchronized (mux) {
      rmv = super.markObsolete(ver);
    }

    // Remove this entry from partition mapping.
    if (rmv) cctx.dht().topology().onRemoved(this);

    return rmv;
  }

  /**
   * Add local candidate.
   *
   * @param nearNodeId Near node ID.
   * @param nearVer Near version.
   * @param threadId Owning thread ID.
   * @param ver Lock version.
   * @param timeout Timeout to acquire lock.
   * @param reenter Reentry flag.
   * @param ec Eventually consistent flag.
   * @param tx Tx flag.
   * @return New candidate.
   * @throws GridCacheEntryRemovedException If entry has been removed.
   * @throws GridDistributedLockCancelledException If lock was cancelled.
   */
  @Nullable
  public GridCacheMvccCandidate<K> addDhtLocal(
      UUID nearNodeId,
      GridCacheVersion nearVer,
      long threadId,
      GridCacheVersion ver,
      long timeout,
      boolean reenter,
      boolean ec,
      boolean tx)
      throws GridCacheEntryRemovedException, GridDistributedLockCancelledException {
    GridCacheMvccCandidate<K> cand;
    GridCacheMvccCandidate<K> prev;
    GridCacheMvccCandidate<K> owner;

    V val;

    synchronized (mux) {
      // Check removed locks prior to obsolete flag.
      checkRemoved(ver);

      checkObsolete();

      prev = mvcc.anyOwner();

      boolean emptyBefore = mvcc.isEmpty();

      cand =
          mvcc.addLocal(this, nearNodeId, nearVer, threadId, ver, timeout, reenter, ec, tx, true);

      owner = mvcc.anyOwner();

      boolean emptyAfter = mvcc.isEmpty();

      if (prev != owner) mux.notifyAll();

      checkCallbacks(emptyBefore, emptyAfter);

      val = rawGet();
    }

    // Don't link reentries.
    if (cand != null && !cand.reentry())
      // Link with other candidates in the same thread.
      cctx.mvcc().addNext(cand);

    checkOwnerChanged(prev, owner, val);

    return cand;
  }

  /** {@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;
    }
  }

  /** {@inheritDoc} */
  @Override
  public GridCacheMvccCandidate<K> removeLock() {
    GridCacheMvccCandidate<K> ret = super.removeLock();

    locPart.onUnlock();

    return ret;
  }

  /** {@inheritDoc} */
  @Override
  public boolean removeLock(GridCacheVersion ver) throws GridCacheEntryRemovedException {
    boolean ret = super.removeLock(ver);

    locPart.onUnlock();

    return ret;
  }

  /**
   * @return Readers.
   * @throws GridCacheEntryRemovedException If removed.
   */
  public Collection<UUID> readers() throws GridCacheEntryRemovedException {
    return F.viewReadOnly(checkReaders(), R2N);
  }

  /**
   * @param nodeId Node ID.
   * @return reader ID.
   */
  @Nullable
  private ReaderId readerId(UUID nodeId) {
    for (ReaderId reader : readers) if (reader.nodeId().equals(nodeId)) return reader;

    return null;
  }

  /**
   * @param nodeId Reader to add.
   * @param msgId Message ID.
   * @return Future for all relevant transactions that were active at the time of adding reader, or
   *     {@code null} if reader was added
   * @throws GridCacheEntryRemovedException If entry was removed.
   */
  @Nullable
  public GridFuture<Boolean> addReader(UUID nodeId, long msgId)
      throws GridCacheEntryRemovedException {
    // Don't add local node as reader.
    if (cctx.nodeId().equals(nodeId)) return null;

    GridNode node = cctx.discovery().node(nodeId);

    // If remote node has no near cache, don't add it.
    if (node == null || !U.hasNearCache(node, cctx.dht().near().name())) return null;

    // If remote node is (primary?) or back up, don't add it as a reader.
    if (U.nodeIds(cctx.affinity(partition(), CU.allNodes(cctx))).contains(nodeId)) return null;

    boolean ret = false;

    GridCacheMultiTxFuture<K, V> txFut;

    Collection<GridCacheMvccCandidate<K>> cands = null;

    synchronized (mux) {
      checkObsolete();

      txFut = this.txFut;

      ReaderId reader = readerId(nodeId);

      if (reader == null) {
        reader = new ReaderId(nodeId, msgId);

        readers = new LinkedList<ReaderId>(readers);

        readers.add(reader);

        // Seal.
        readers = Collections.unmodifiableList(readers);

        txFut = this.txFut = new GridCacheMultiTxFuture<K, V>(cctx);

        cands = localCandidates();

        ret = true;
      } else {
        long id = reader.messageId();

        if (id < msgId) reader.messageId(msgId);
      }
    }

    if (ret) {
      assert txFut != null;

      if (!F.isEmpty(cands)) {
        for (GridCacheMvccCandidate<K> c : cands) {
          GridCacheTxEx<K, V> tx = cctx.tm().<GridCacheTxEx<K, V>>tx(c.version());

          if (tx != null) {
            assert tx.local();

            txFut.addTx(tx);
          }
        }
      }

      txFut.init();

      if (!txFut.isDone()) {
        txFut.listenAsync(
            new CI1<GridFuture<?>>() {
              @Override
              public void apply(GridFuture<?> f) {
                synchronized (mux) {
                  // Release memory.
                  GridDhtCacheEntry.this.txFut = null;
                }
              }
            });
      } else
        // Release memory.
        txFut = this.txFut = null;
    }

    return txFut;
  }

  /**
   * @param nodeId Reader to remove.
   * @param msgId Message ID.
   * @return {@code True} if reader was removed as a result of this operation.
   * @throws GridCacheEntryRemovedException If entry was removed.
   */
  public boolean removeReader(UUID nodeId, long msgId) throws GridCacheEntryRemovedException {
    synchronized (mux) {
      checkObsolete();

      ReaderId reader = readerId(nodeId);

      if (reader == null || reader.messageId() > msgId) return false;

      readers = new LinkedList<ReaderId>(readers);

      readers.remove(reader);

      // Seal.
      readers = Collections.unmodifiableList(readers);

      return true;
    }
  }

  /** Clears all readers (usually when partition becomes invalid and ready for eviction). */
  @Override
  public void clearReaders() {
    synchronized (mux) {
      readers = Collections.emptyList();
    }
  }

  /**
   * @return Collection of readers after check.
   * @throws GridCacheEntryRemovedException If removed.
   */
  public Collection<ReaderId> checkReaders() throws GridCacheEntryRemovedException {
    synchronized (mux) {
      checkObsolete();

      if (!readers.isEmpty()) {
        List<ReaderId> rmv = null;

        for (ReaderId reader : readers) {
          if (!cctx.discovery().alive(reader.nodeId())) {
            if (rmv == null) rmv = new LinkedList<ReaderId>();

            rmv.add(reader);
          }
        }

        if (rmv != null) {
          readers = new LinkedList<ReaderId>(readers);

          for (ReaderId rdr : rmv) readers.remove(rdr);

          readers = Collections.unmodifiableList(readers);
        }
      }

      return readers;
    }
  }

  /** {@inheritDoc} */
  @Override
  protected boolean hasReaders() throws GridCacheEntryRemovedException {
    synchronized (mux) {
      checkReaders();

      return !readers.isEmpty();
    }
  }

  /**
   * @param ver Version.
   * @param mappings Mappings to set.
   * @return Candidate, if one existed for the version, or {@code null} if candidate was not found.
   * @throws GridCacheEntryRemovedException If removed.
   */
  public GridCacheMvccCandidate<K> mappings(GridCacheVersion ver, Collection<UUID> mappings)
      throws GridCacheEntryRemovedException {
    synchronized (mux) {
      checkObsolete();

      GridCacheMvccCandidate<K> cand = mvcc.candidate(ver);

      if (cand != null) cand.mappedNodeIds(mappings);

      return cand;
    }
  }

  /** {@inheritDoc} */
  @Override
  public GridCacheEntry<K, V> wrap(boolean prjAware) {
    GridCacheContext<K, V> nearCtx = cctx.dht().near().context();

    GridCacheProjectionImpl<K, V> prjPerCall = nearCtx.projectionPerCall();

    if (prjPerCall != null && prjAware)
      return new GridPartitionedCacheEntryImpl<K, V>(prjPerCall, nearCtx, key, this);

    GridCacheEntryImpl<K, V> wrapper = this.wrapper;

    if (wrapper == null)
      this.wrapper = wrapper = new GridPartitionedCacheEntryImpl<K, V>(null, nearCtx, key, this);

    return wrapper;
  }

  /** {@inheritDoc} */
  @Override
  public String toString() {
    return S.toString(GridDhtCacheEntry.class, this, "super", super.toString());
  }

  /** Reader ID. */
  private static class ReaderId {
    /** Node ID. */
    private UUID nodeId;

    /** Message ID. */
    private long msgId;

    /**
     * @param nodeId Node ID.
     * @param msgId Message ID.
     */
    ReaderId(UUID nodeId, long msgId) {
      this.nodeId = nodeId;
      this.msgId = msgId;
    }

    /** @return Node ID. */
    UUID nodeId() {
      return nodeId;
    }

    /** @return Message ID. */
    long messageId() {
      return msgId;
    }

    /** @param msgId Message ID. */
    void messageId(long msgId) {
      this.msgId = msgId;
    }

    /** {@inheritDoc} */
    @Override
    public String toString() {
      return S.toString(ReaderId.class, this);
    }
  }
}
示例#15
0
  /**
   * @param nodeId Reader to add.
   * @param msgId Message ID.
   * @return Future for all relevant transactions that were active at the time of adding reader, or
   *     {@code null} if reader was added
   * @throws GridCacheEntryRemovedException If entry was removed.
   */
  @Nullable
  public GridFuture<Boolean> addReader(UUID nodeId, long msgId)
      throws GridCacheEntryRemovedException {
    // Don't add local node as reader.
    if (cctx.nodeId().equals(nodeId)) return null;

    GridNode node = cctx.discovery().node(nodeId);

    // If remote node has no near cache, don't add it.
    if (node == null || !U.hasNearCache(node, cctx.dht().near().name())) return null;

    // If remote node is (primary?) or back up, don't add it as a reader.
    if (U.nodeIds(cctx.affinity(partition(), CU.allNodes(cctx))).contains(nodeId)) return null;

    boolean ret = false;

    GridCacheMultiTxFuture<K, V> txFut;

    Collection<GridCacheMvccCandidate<K>> cands = null;

    synchronized (mux) {
      checkObsolete();

      txFut = this.txFut;

      ReaderId reader = readerId(nodeId);

      if (reader == null) {
        reader = new ReaderId(nodeId, msgId);

        readers = new LinkedList<ReaderId>(readers);

        readers.add(reader);

        // Seal.
        readers = Collections.unmodifiableList(readers);

        txFut = this.txFut = new GridCacheMultiTxFuture<K, V>(cctx);

        cands = localCandidates();

        ret = true;
      } else {
        long id = reader.messageId();

        if (id < msgId) reader.messageId(msgId);
      }
    }

    if (ret) {
      assert txFut != null;

      if (!F.isEmpty(cands)) {
        for (GridCacheMvccCandidate<K> c : cands) {
          GridCacheTxEx<K, V> tx = cctx.tm().<GridCacheTxEx<K, V>>tx(c.version());

          if (tx != null) {
            assert tx.local();

            txFut.addTx(tx);
          }
        }
      }

      txFut.init();

      if (!txFut.isDone()) {
        txFut.listenAsync(
            new CI1<GridFuture<?>>() {
              @Override
              public void apply(GridFuture<?> f) {
                synchronized (mux) {
                  // Release memory.
                  GridDhtCacheEntry.this.txFut = null;
                }
              }
            });
      } else
        // Release memory.
        txFut = this.txFut = null;
    }

    return txFut;
  }
  /** 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();
  }