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