protected void updateStateOnNodesLeaving(Collection<Address> leavers) { Set<GlobalTransaction> toKill = new HashSet<GlobalTransaction>(); for (GlobalTransaction gt : remoteTransactions.keySet()) { if (leavers.contains(gt.getAddress())) toKill.add(gt); } if (toKill.isEmpty()) log.tracef( "No global transactions pertain to originator(s) %s who have left the cluster.", leavers); else log.tracef( "%s global transactions pertain to leavers list %s and need to be killed", toKill.size(), leavers); for (GlobalTransaction gtx : toKill) { log.tracef("Killing remote transaction originating on leaver %s", gtx); RollbackCommand rc = new RollbackCommand(cacheName, gtx); rc.init(invoker, icc, TransactionTable.this); try { rc.perform(null); log.tracef("Rollback of transaction %s complete.", gtx); } catch (Throwable e) { log.unableToRollbackGlobalTx(gtx, e); } } log.trace("Completed cleaning transactions originating on leavers"); }
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; } }
public void cleanupLeaverTransactions(List<Address> members) { // Can happen if the cache is non-transactional if (remoteTransactions == null) return; if (trace) log.tracef( "Checking for transactions originated on leavers. Current cache members are %s, remote transactions: %d", members, remoteTransactions.size()); HashSet<Address> membersSet = new HashSet<>(members); List<GlobalTransaction> toKill = new ArrayList<>(); for (Map.Entry<GlobalTransaction, RemoteTransaction> e : remoteTransactions.entrySet()) { GlobalTransaction gt = e.getKey(); if (trace) log.tracef("Checking transaction %s", gt); if (!membersSet.contains(gt.getAddress())) { toKill.add(gt); } } if (toKill.isEmpty()) { if (trace) log.tracef("No remote transactions pertain to originator(s) who have left the cluster."); } else { log.debugf("The originating node left the cluster for %d remote transactions", toKill.size()); for (GlobalTransaction gtx : toKill) { if (partitionHandlingManager.canRollbackTransactionAfterOriginatorLeave(gtx)) { log.debugf( "Rolling back transaction %s because originator %s left the cluster", gtx, gtx.getAddress()); killTransaction(gtx); } else { log.debugf( "Keeping transaction %s after the originator %s left the cluster.", gtx, gtx.getAddress()); } } if (trace) log.tracef( "Completed cleaning transactions originating on leavers. Remote transactions remaining: %d", remoteTransactions.size()); } }
/** @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; }
public void cleanupStaleTransactions(CacheTopology cacheTopology) { int topologyId = cacheTopology.getTopologyId(); List<Address> members = cacheTopology.getMembers(); // We only care about transactions originated before this topology update if (getMinTopologyId() >= topologyId) return; log.tracef( "Checking for transactions originated on leavers. Current members are %s, remote transactions: %d", members, remoteTransactions.size()); Set<GlobalTransaction> toKill = new HashSet<GlobalTransaction>(); for (Map.Entry<GlobalTransaction, RemoteTransaction> e : remoteTransactions.entrySet()) { GlobalTransaction gt = e.getKey(); RemoteTransaction remoteTx = e.getValue(); log.tracef("Checking transaction %s", gt); // The topology id check is needed for joiners if (remoteTx.getTopologyId() < topologyId && !members.contains(gt.getAddress())) { toKill.add(gt); } } if (toKill.isEmpty()) { log.tracef("No global transactions pertain to originator(s) who have left the cluster."); } else { log.tracef("%s global transactions pertain to leavers and need to be killed", toKill.size()); } for (GlobalTransaction gtx : toKill) { log.tracef("Killing remote transaction originating on leaver %s", gtx); RollbackCommand rc = new RollbackCommand(cacheName, gtx); rc.init(invoker, icc, TransactionTable.this); try { rc.perform(null); log.tracef("Rollback of transaction %s complete.", gtx); } catch (Throwable e) { log.unableToRollbackGlobalTx(gtx, e); } } log.tracef( "Completed cleaning transactions originating on leavers. Remote transactions remaining: %d", remoteTransactions.size()); }