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; } }
private CompletableFuture<Void> visitSecondPhaseCommand( TxInvocationContext ctx, TransactionBoundaryCommand command, boolean commit, ExtendedStatistic duration, ExtendedStatistic counter) throws Throwable { GlobalTransaction globalTransaction = command.getGlobalTransaction(); if (trace) { log.tracef( "Visit 2nd phase command %s. Is it local? %s. Transaction is %s", command, ctx.isOriginLocal(), globalTransaction.globalId()); } long start = timeService.time(); return ctx.onReturn( (rCtx, rCommand, rv, throwable) -> { if (throwable != null) { throw throwable; } long end = timeService.time(); updateTime(duration, counter, start, end, globalTransaction, rCtx.isOriginLocal()); cacheStatisticManager.setTransactionOutcome( commit, globalTransaction, rCtx.isOriginLocal()); cacheStatisticManager.terminateTransaction(globalTransaction, true, true); return null; }); }
/** @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; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EventImpl<?, ?> event = (EventImpl<?, ?>) o; if (originLocal != event.originLocal) return false; if (pre != event.pre) return false; if (transactionSuccessful != event.transactionSuccessful) return false; if (cache != null ? !cache.equals(event.cache) : event.cache != null) return false; if (key != null ? !key.equals(event.key) : event.key != null) return false; if (transaction != null ? !transaction.equals(event.transaction) : event.transaction != null) return false; if (type != event.type) return false; if (value != null ? !value.equals(event.value) : event.value != null) return false; if (!Util.safeEquals(consistentHashAtStart, event.consistentHashAtStart)) return false; if (!Util.safeEquals(consistentHashAtEnd, event.consistentHashAtEnd)) return false; if (!Util.safeEquals(unionConsistentHash, event.unionConsistentHash)) return false; if (newTopologyId != event.newTopologyId) return false; if (created != event.created) return false; if (oldValue != null ? !oldValue.equals(event.oldValue) : event.oldValue != null) return false; return true; }
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()); }
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()); } }
@Override public CompletableFuture<Void> visitPrepareCommand( TxInvocationContext ctx, PrepareCommand command) throws Throwable { GlobalTransaction globalTransaction = command.getGlobalTransaction(); if (trace) { log.tracef( "Visit Prepare command %s. Is it local?. Transaction is %s", command, ctx.isOriginLocal(), globalTransaction.globalId()); } initStatsIfNecessary(ctx); cacheStatisticManager.onPrepareCommand(globalTransaction, ctx.isOriginLocal()); if (command.hasModifications()) { cacheStatisticManager.markAsWriteTransaction(globalTransaction, ctx.isOriginLocal()); } long start = timeService.time(); return ctx.onReturn( (rCtx, rCommand, rv, throwable) -> { if (throwable != null) { processWriteException(rCtx, globalTransaction, throwable); } else { long end = timeService.time(); updateTime( PREPARE_EXECUTION_TIME, NUM_PREPARE_COMMAND, start, end, globalTransaction, rCtx.isOriginLocal()); } if (((PrepareCommand) rCommand).isOnePhaseCommit()) { boolean local = rCtx.isOriginLocal(); boolean success = throwable == null; cacheStatisticManager.setTransactionOutcome( success, globalTransaction, rCtx.isOriginLocal()); cacheStatisticManager.terminateTransaction(globalTransaction, local, !local); } return null; }); }
@Override public int hashCode() { int result = (pre ? 1 : 0); result = 31 * result + (cache != null ? cache.hashCode() : 0); result = 31 * result + (key != null ? key.hashCode() : 0); result = 31 * result + (transaction != null ? transaction.hashCode() : 0); result = 31 * result + (originLocal ? 1 : 0); result = 31 * result + (transactionSuccessful ? 1 : 0); result = 31 * result + (type != null ? type.hashCode() : 0); result = 31 * result + (value != null ? value.hashCode() : 0); result = 31 * result + (membersAtStart != null ? membersAtStart.hashCode() : 0); result = 31 * result + (membersAtEnd != null ? membersAtEnd.hashCode() : 0); result = 31 * result + (consistentHashAtStart != null ? consistentHashAtStart.hashCode() : 0); result = 31 * result + (consistentHashAtEnd != null ? consistentHashAtEnd.hashCode() : 0); result = 31 * result + ((int) newViewId); return result; }
@Override public int hashCode() { int result = (pre ? 1 : 0); result = 31 * result + (cache != null ? cache.hashCode() : 0); result = 31 * result + (key != null ? key.hashCode() : 0); result = 31 * result + (transaction != null ? transaction.hashCode() : 0); result = 31 * result + (originLocal ? 1 : 0); result = 31 * result + (transactionSuccessful ? 1 : 0); result = 31 * result + (type != null ? type.hashCode() : 0); result = 31 * result + (value != null ? value.hashCode() : 0); result = 31 * result + (consistentHashAtStart != null ? consistentHashAtStart.hashCode() : 0); result = 31 * result + (consistentHashAtEnd != null ? consistentHashAtEnd.hashCode() : 0); result = 31 * result + (unionConsistentHash != null ? unionConsistentHash.hashCode() : 0); result = 31 * result + newTopologyId; result = 31 * result + (created ? 1 : 0); result = 31 * result + (oldValue != null ? oldValue.hashCode() : 0); return result; }