/** {@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();
 }
  /**
   * @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;
  }
Beispiel #4
0
 /** @return Collection of recovery writes. */
 public Collection<GridCacheTxEntry<K, V>> recoveryWrites() {
   return recoveryWrites == null
       ? Collections.<GridCacheTxEntry<K, V>>emptyList()
       : recoveryWrites;
 }
 /** Clears all readers (usually when partition becomes invalid and ready for eviction). */
 @Override
 public void clearReaders() {
   synchronized (mux) {
     readers = Collections.emptyList();
   }
 }
/**
 * 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);
    }
  }
}