public CompletedTransactionStatus getTransactionStatus(GlobalTransaction gtx) {
      CompletedTransactionInfo completedTx = completedTransactions.get(gtx);
      if (completedTx != null) {
        return completedTx.successful
            ? CompletedTransactionStatus.COMMITTED
            : CompletedTransactionStatus.ABORTED;
      }

      // Transaction ids are allocated in sequence, so any transaction with a smaller id must have
      // been started
      // before a transaction that was already removed from the completed transactions map because
      // it was too old.
      // We assume that the transaction was either committed, or it was rolled back (e.g. because
      // the prepare
      // RPC timed out.
      // Note: We must check the id *after* verifying that the tx doesn't exist in the map.
      if (gtx.getId() > globalMaxPrunedTxId) return CompletedTransactionStatus.NOT_COMPLETED;
      Long nodeMaxPrunedTxId = nodeMaxPrunedTxIds.get(gtx.getAddress());
      if (nodeMaxPrunedTxId == null) {
        // We haven't removed any transaction for this node
        return CompletedTransactionStatus.NOT_COMPLETED;
      } else if (gtx.getId() > nodeMaxPrunedTxId) {
        // We haven't removed this particular transaction yet
        return CompletedTransactionStatus.NOT_COMPLETED;
      } else {
        // We already removed the status of this transaction from the completed transactions map
        return CompletedTransactionStatus.EXPIRED;
      }
    }
    /** @see #markTransactionCompleted(GlobalTransaction, boolean) */
    public boolean isTransactionCompleted(GlobalTransaction gtx) {
      if (completedTransactions.containsKey(gtx)) return true;

      // Transaction ids are allocated in sequence, so any transaction with a smaller id must have
      // been started
      // before a transaction that was already removed from the completed transactions map because
      // it was too old.
      // We assume that the transaction was either committed, or it was rolled back (e.g. because
      // the prepare
      // RPC timed out.
      // Note: We must check the id *after* verifying that the tx doesn't exist in the map.
      if (gtx.getId() > globalMaxPrunedTxId) return false;
      Long nodeMaxPrunedTxId = nodeMaxPrunedTxIds.get(gtx.getAddress());
      return nodeMaxPrunedTxId != null && gtx.getId() <= nodeMaxPrunedTxId;
    }
Esempio n. 3
0
 /**
  * It stops tracking keys committed.
  *
  * @param track Flag to stop tracking keys for local site state transfer or for remote site state
  *     transfer.
  */
 public final void stopTrack(Flag track) {
   setTrack(track, false);
   if (!trackStateTransfer && !trackXSiteStateTransfer) {
     if (trace) {
       log.tracef("Tracking is disabled. Clear tracker: %s", tracker);
     }
     tracker.clear();
   } else {
     for (Iterator<Map.Entry<Object, DiscardPolicy>> iterator = tracker.entrySet().iterator();
         iterator.hasNext(); ) {
       if (iterator.next().getValue().update(trackStateTransfer, trackXSiteStateTransfer)) {
         iterator.remove();
       }
     }
   }
 }
Esempio n. 4
0
 /**
  * It tries to commit the cache entry. The entry is not committed if it is originated from state
  * transfer and other operation already has updated it.
  *
  * @param entry the entry to commit
  * @param metadata the entry's metadata
  * @param operation if {@code null}, it identifies this commit as originated from a normal
  *     operation. Otherwise, it is originated from a state transfer (local or remote site)
  */
 public final void commit(
     final CacheEntry entry,
     final Metadata metadata,
     final Flag operation,
     boolean l1Invalidation) {
   if (trace) {
     log.tracef(
         "Trying to commit. Key=%s. Operation Flag=%s, L1 invalidation=%s",
         toStr(entry.getKey()), operation, l1Invalidation);
   }
   if (l1Invalidation || (operation == null && !trackStateTransfer && !trackXSiteStateTransfer)) {
     // track == null means that it is a normal put and the tracking is not enabled!
     // if it is a L1 invalidation, commit without track it.
     if (trace) {
       log.tracef(
           "Committing key=%s. It is a L1 invalidation or a normal put and no tracking is enabled!",
           toStr(entry.getKey()));
     }
     entry.commit(dataContainer, metadata);
     return;
   }
   if (isTrackDisabled(operation)) {
     // this a put for state transfer but we are not tracking it. This means that the state
     // transfer has ended
     // or canceled due to a clear command.
     if (trace) {
       log.tracef(
           "Not committing key=%s. It is a state transfer key but no track is enabled!",
           toStr(entry.getKey()));
     }
     return;
   }
   tracker.compute(
       entry.getKey(),
       (o, discardPolicy) -> {
         if (discardPolicy != null && discardPolicy.ignore(operation)) {
           if (trace) {
             log.tracef(
                 "Not committing key=%s. It was already overwritten! Discard policy=%s",
                 toStr(entry.getKey()), discardPolicy);
           }
           return discardPolicy;
         }
         entry.commit(dataContainer, metadata);
         DiscardPolicy newDiscardPolicy = calculateDiscardPolicy();
         if (trace) {
           log.tracef(
               "Committed key=%s. Old discard policy=%s. New discard policy=%s",
               toStr(entry.getKey()), discardPolicy, newDiscardPolicy);
         }
         return newDiscardPolicy;
       });
 }
Esempio n. 5
0
 @Override
 public String toString() {
   return "CommitManager{"
       + "tracker="
       + tracker.size()
       + " key(s)"
       + ", trackStateTransfer="
       + trackStateTransfer
       + ", trackXSiteStateTransfer="
       + trackXSiteStateTransfer
       + '}';
 }
 private void updateLastPrunedTxId(final long txId, Address address) {
   if (txId > globalMaxPrunedTxId) {
     globalMaxPrunedTxId = txId;
   }
   nodeMaxPrunedTxIds.compute(
       address,
       (a, nodeMaxPrunedTxId) -> {
         if (nodeMaxPrunedTxId != null && txId <= nodeMaxPrunedTxId) {
           return nodeMaxPrunedTxId;
         }
         return txId;
       });
 }
Esempio n. 7
0
 /** @return {@code true} if no keys are tracked, {@code false} otherwise. */
 public final boolean isEmpty() {
   return tracker.isEmpty();
 }
    public void cleanupCompletedTransactions() {
      if (completedTransactions.isEmpty()) return;

      try {
        if (trace)
          log.tracef(
              "About to cleanup completed transaction. Initial size is %d",
              completedTransactions.size());
        long beginning = timeService.time();
        long minCompleteTimestamp =
            timeService.time()
                - TimeUnit.MILLISECONDS.toNanos(configuration.transaction().completedTxTimeout());
        int removedEntries = 0;

        // Collect the leavers. They will be removed at the end.
        Set<Address> leavers = new HashSet<>();
        for (Map.Entry<Address, Long> e : nodeMaxPrunedTxIds.entrySet()) {
          if (!rpcManager.getMembers().contains(e.getKey())) {
            leavers.add(e.getKey());
          }
        }

        // Remove stale completed transactions.
        Iterator<Map.Entry<GlobalTransaction, CompletedTransactionInfo>> txIterator =
            completedTransactions.entrySet().iterator();
        while (txIterator.hasNext()) {
          Map.Entry<GlobalTransaction, CompletedTransactionInfo> e = txIterator.next();
          CompletedTransactionInfo completedTx = e.getValue();
          if (minCompleteTimestamp - completedTx.timestamp > 0) {
            // Need to update lastPrunedTxId *before* removing the tx from the map
            // Don't need atomic operations, there can't be more than one thread updating
            // lastPrunedTxId.
            final long txId = e.getKey().getId();
            final Address address = e.getKey().getAddress();
            updateLastPrunedTxId(txId, address);

            txIterator.remove();
            removedEntries++;
          } else {
            // Nodes with "active" completed transactions are not removed..
            leavers.remove(e.getKey().getAddress());
          }
        }

        // Finally, remove nodes that are no longer members and don't have any "active" completed
        // transactions.
        for (Address e : leavers) {
          nodeMaxPrunedTxIds.remove(e);
        }

        long duration = timeService.timeDuration(beginning, TimeUnit.MILLISECONDS);

        if (trace)
          log.tracef(
              "Finished cleaning up completed transactions in %d millis, %d transactions were removed, "
                  + "current number of completed transactions is %d",
              removedEntries, duration, completedTransactions.size());
        if (trace)
          log.tracef(
              "Last pruned transaction ids were updated: %d, %s",
              globalMaxPrunedTxId, nodeMaxPrunedTxIds);
      } catch (Exception e) {
        log.errorf(e, "Failed to cleanup completed transactions: %s", e.getMessage());
      }
    }
 /**
  * With the current state transfer implementation it is possible for a transaction to be
  * prepared several times on a remote node. This might cause leaks, e.g. if the transaction is
  * prepared, committed and prepared again. Once marked as completed (because of commit or
  * rollback) any further prepare received on that transaction are discarded.
  */
 public void markTransactionCompleted(GlobalTransaction globalTx, boolean successful) {
   if (trace) log.tracef("Marking transaction %s as completed", globalTx);
   completedTransactions.put(
       globalTx, new CompletedTransactionInfo(timeService.time(), successful));
 }