/**
   * This method calculates the minimum view ID known by the current node. This method is only used
   * in a clustered cache, and only invoked when either a view change is detected, or a transaction
   * whose view ID is not the same as the current view ID.
   *
   * <p>This method is guarded by minViewRecalculationLock to prevent concurrent updates to the
   * minimum view ID field.
   *
   * @param idOfRemovedTransaction the view ID associated with the transaction that triggered this
   *     recalculation, or -1 if triggered by a view change event.
   */
  @GuardedBy("minViewRecalculationLock")
  private void calculateMinViewId(int idOfRemovedTransaction) {
    minViewRecalculationLock.lock();
    try {
      // We should only need to re-calculate the minimum view ID if the transaction being completed
      // has the same ID as the smallest known transaction ID, to check what the new smallest is.
      // We do this check
      // again here, since this is now within a synchronized method.
      if (idOfRemovedTransaction == -1
          || (idOfRemovedTransaction == minTxViewId && idOfRemovedTransaction < currentViewId)) {
        int minViewIdFound = currentViewId;

        for (CacheTransaction ct : localTransactions.values()) {
          int viewId = ct.getViewId();
          if (viewId < minViewIdFound) minViewIdFound = viewId;
        }
        for (CacheTransaction ct : remoteTransactions.values()) {
          int viewId = ct.getViewId();
          if (viewId < minViewIdFound) minViewIdFound = viewId;
        }
        if (minViewIdFound > minTxViewId) {
          log.tracef("Changing minimum view ID from %s to %s", minTxViewId, minViewIdFound);
          minTxViewId = minViewIdFound;
        } else {
          log.tracef("Minimum view ID still is %s; nothing to change", minViewIdFound);
        }
      }
    } finally {
      minViewRecalculationLock.unlock();
    }
  }
 private void releaseResources(CacheTransaction cacheTransaction) {
   if (cacheTransaction != null) {
     if (clustered) {
       recalculateMinViewIdIfNeeded(cacheTransaction);
     }
     log.tracef("Removed %s from transaction table.", cacheTransaction);
     cacheTransaction.notifyOnTransactionFinished();
   }
 }
 private void applyTransactions(Address sender, Collection<TransactionInfo> transactions) {
   log.debugf(
       "Applying %d transactions for cache %s transferred from node %s",
       transactions.size(), cacheName, sender);
   if (isTransactional) {
     for (TransactionInfo transactionInfo : transactions) {
       CacheTransaction tx =
           transactionTable.getLocalTransaction(transactionInfo.getGlobalTransaction());
       if (tx == null) {
         tx = transactionTable.getRemoteTransaction(transactionInfo.getGlobalTransaction());
         if (tx == null) {
           tx =
               transactionTable.getOrCreateRemoteTransaction(
                   transactionInfo.getGlobalTransaction(), transactionInfo.getModifications());
           ((RemoteTransaction) tx).setMissingLookedUpEntries(true);
         }
       }
       for (Object key : transactionInfo.getLockedKeys()) {
         tx.addBackupLockForKey(key);
       }
     }
   }
 }
    private EntryVersionsMap clusteredCreateNewVersionsAndCheckForWriteSkews(
        VersionGenerator versionGenerator,
        TxInvocationContext context,
        VersionedPrepareCommand prepareCommand) {
      // Perform a write skew check on mapped entries.
      EntryVersionsMap uv =
          performWriteSkewCheckAndReturnNewVersions(
              prepareCommand,
              dataContainer,
              persistenceManager,
              versionGenerator,
              context,
              keySpecificLogic,
              timeService);

      CacheTransaction cacheTransaction = context.getCacheTransaction();
      EntryVersionsMap uvOld = cacheTransaction.getUpdatedEntryVersions();
      if (uvOld != null && !uvOld.isEmpty()) {
        uvOld.putAll(uv);
        uv = uvOld;
      }
      cacheTransaction.setUpdatedEntryVersions(uv);
      return (uv.isEmpty()) ? null : uv;
    }
  protected final void recalculateMinViewIdIfNeeded(CacheTransaction removedTransaction) {
    if (removedTransaction == null)
      throw new IllegalArgumentException("Transaction cannot be null!");
    if (currentViewId != CACHE_STOPPED_VIEW_ID) {

      // Assume that we only get here if we are clustered.
      int removedTransactionViewId = removedTransaction.getViewId();
      if (removedTransactionViewId < minTxViewId) {
        log.tracef(
            "A transaction has a view ID (%s) that is smaller than the smallest transaction view ID (%s) this node knows about!  This can happen if a concurrent thread recalculates the minimum view ID after the current transaction has been removed from the transaction table.",
            removedTransactionViewId, minTxViewId);
      } else if (removedTransactionViewId == minTxViewId
          && removedTransactionViewId < currentViewId) {
        // We should only need to re-calculate the minimum view ID if the transaction being
        // completed
        // has the same ID as the smallest known transaction ID, to check what the new smallest is,
        // and this is
        // not the current view ID.
        calculateMinViewId(removedTransactionViewId);
      }
    }
  }
 private static int preComputeHashCode(final CacheTransaction cacheTx) {
   return 31 + cacheTx.hashCode();
 }