@Override public Object visitCommitCommand(TxInvocationContext ctx, CommitCommand command) throws Throwable { if (ctx.getCacheTransaction() instanceof RemoteTransaction) { // If a commit is received for a transaction that doesn't have its 'lookedUpEntries' populated // we know for sure this transaction is 2PC and was received via state transfer but the // preceding PrepareCommand // was not received by local node because it was executed on the previous key owners. We need // to re-prepare // the transaction on local node to ensure its locks are acquired and lookedUpEntries is // properly populated. RemoteTransaction remoteTx = (RemoteTransaction) ctx.getCacheTransaction(); if (remoteTx.isMissingLookedUpEntries()) { remoteTx.setMissingLookedUpEntries(false); PrepareCommand prepareCommand; if (useVersioning) { prepareCommand = commandFactory.buildVersionedPrepareCommand( ctx.getGlobalTransaction(), ctx.getModifications(), false); } else { prepareCommand = commandFactory.buildPrepareCommand( ctx.getGlobalTransaction(), ctx.getModifications(), false); } commandFactory.initializeReplicableCommand(prepareCommand, true); prepareCommand.setOrigin(ctx.getOrigin()); log.tracef( "Replaying the transactions received as a result of state transfer %s", prepareCommand); prepareCommand.perform(null); } } return handleTxCommand(ctx, command); }
@Override @SuppressWarnings("unchecked") public Object clone() { try { RemoteTransaction dolly = (RemoteTransaction) super.clone(); dolly.modifications = new ArrayList<WriteCommand>(modifications); dolly.lookedUpEntries = new HashMap<Object, CacheEntry>(lookedUpEntries); return dolly; } catch (CloneNotSupportedException e) { throw new IllegalStateException("Impossible!!"); } }
private void registerRemoteTransaction(GlobalTransaction gtx, RemoteTransaction rtx) { RemoteTransaction transaction = remoteTransactions.put(gtx, rtx); if (transaction != null) { log.remoteTxAlreadyRegistered(); throw new IllegalStateException( "A remote transaction with the given id was already registered!!!"); } log.tracef("Created and registered remote transaction %s", rtx); if (rtx.getTopologyId() < minTxTopologyId) { log.tracef( "Changing minimum topology ID from %d to %d", minTxTopologyId, rtx.getTopologyId()); minTxTopologyId = rtx.getTopologyId(); } }
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()); }
private RemoteTransaction getOrCreateRemoteTransaction( GlobalTransaction globalTx, WriteCommand[] modifications, int topologyId) { RemoteTransaction remoteTransaction = remoteTransactions.get(globalTx); if (remoteTransaction != null) return remoteTransaction; remoteTransaction = modifications == null ? txFactory.newRemoteTransaction(globalTx, topologyId) : txFactory.newRemoteTransaction(modifications, globalTx, topologyId); RemoteTransaction existing = remoteTransactions.putIfAbsent(globalTx, remoteTransaction); if (existing != null) { log.tracef("Remote transaction already registered: %s", existing); return existing; } else { log.tracef("Created and registered remote transaction %s", remoteTransaction); if (remoteTransaction.getTopologyId() < minTxTopologyId) { log.tracef( "Changing minimum topology ID from %d to %d", minTxTopologyId, remoteTransaction.getTopologyId()); minTxTopologyId = remoteTransaction.getTopologyId(); } return remoteTransaction; } }
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); } } } }
public Set<Object> getLockedKeysForRemoteTransaction(GlobalTransaction gtx) { RemoteTransaction transaction = remoteTransactions.get(gtx); if (transaction == null) return emptySet(); return transaction.getLockedKeys(); }
/** @param isRemote true if the command is deserialized and is executed remote. */ public void initializeReplicableCommand(ReplicableCommand c, boolean isRemote) { if (c == null) return; switch (c.getCommandId()) { case PutKeyValueCommand.COMMAND_ID: ((PutKeyValueCommand) c).init(notifier); break; case PutMapCommand.COMMAND_ID: ((PutMapCommand) c).init(notifier); break; case RemoveCommand.COMMAND_ID: ((RemoveCommand) c).init(notifier); break; case MultipleRpcCommand.COMMAND_ID: MultipleRpcCommand rc = (MultipleRpcCommand) c; rc.init(interceptorChain, icc); if (rc.getCommands() != null) for (ReplicableCommand nested : rc.getCommands()) { initializeReplicableCommand(nested, false); } break; case SingleRpcCommand.COMMAND_ID: SingleRpcCommand src = (SingleRpcCommand) c; src.init(interceptorChain, icc); if (src.getCommand() != null) initializeReplicableCommand(src.getCommand(), false); break; case InvalidateCommand.COMMAND_ID: InvalidateCommand ic = (InvalidateCommand) c; ic.init(notifier); break; case InvalidateL1Command.COMMAND_ID: InvalidateL1Command ilc = (InvalidateL1Command) c; ilc.init(configuration, distributionManager, notifier, dataContainer); break; case PrepareCommand.COMMAND_ID: PrepareCommand pc = (PrepareCommand) c; pc.init(interceptorChain, icc, txTable); pc.initialize(notifier, recoveryManager); if (pc.getModifications() != null) for (ReplicableCommand nested : pc.getModifications()) { initializeReplicableCommand(nested, false); } pc.markTransactionAsRemote(isRemote); if (configuration.isEnableDeadlockDetection() && isRemote) { DldGlobalTransaction transaction = (DldGlobalTransaction) pc.getGlobalTransaction(); transaction.setLocksHeldAtOrigin(pc.getAffectedKeys()); } break; case CommitCommand.COMMAND_ID: CommitCommand commitCommand = (CommitCommand) c; commitCommand.init(interceptorChain, icc, txTable); commitCommand.markTransactionAsRemote(isRemote); break; case RollbackCommand.COMMAND_ID: RollbackCommand rollbackCommand = (RollbackCommand) c; rollbackCommand.init(interceptorChain, icc, txTable); rollbackCommand.markTransactionAsRemote(isRemote); break; case ClearCommand.COMMAND_ID: ClearCommand cc = (ClearCommand) c; cc.init(notifier); break; case ClusteredGetCommand.COMMAND_ID: ClusteredGetCommand clusteredGetCommand = (ClusteredGetCommand) c; clusteredGetCommand.initialize(icc, this, interceptorChain, distributionManager); break; case LockControlCommand.COMMAND_ID: LockControlCommand lcc = (LockControlCommand) c; lcc.init(interceptorChain, icc, txTable); lcc.markTransactionAsRemote(isRemote); if (configuration.isEnableDeadlockDetection() && isRemote) { DldGlobalTransaction gtx = (DldGlobalTransaction) lcc.getGlobalTransaction(); RemoteTransaction transaction = txTable.getRemoteTransaction(gtx); if (transaction != null) { if (!configuration.getCacheMode().isDistributed()) { Set<Object> keys = txTable.getLockedKeysForRemoteTransaction(gtx); GlobalTransaction gtx2 = transaction.getGlobalTransaction(); ((DldGlobalTransaction) gtx2).setLocksHeldAtOrigin(keys); gtx.setLocksHeldAtOrigin(keys); } else { GlobalTransaction gtx2 = transaction.getGlobalTransaction(); ((DldGlobalTransaction) gtx2).setLocksHeldAtOrigin(gtx.getLocksHeldAtOrigin()); } } } break; case RehashControlCommand.COMMAND_ID: RehashControlCommand rcc = (RehashControlCommand) c; rcc.init(distributionManager, configuration, dataContainer, this); break; case GetInDoubtTransactionsCommand.COMMAND_ID: GetInDoubtTransactionsCommand gptx = (GetInDoubtTransactionsCommand) c; gptx.init(recoveryManager); break; case RemoveRecoveryInfoCommand.COMMAND_ID: RemoveRecoveryInfoCommand ftx = (RemoveRecoveryInfoCommand) c; ftx.init(recoveryManager); break; case MapReduceCommand.COMMAND_ID: MapReduceCommand mrc = (MapReduceCommand) c; mrc.init( this, interceptorChain, icc, distributionManager, cache.getAdvancedCache().getRpcManager().getAddress()); break; case DistributedExecuteCommand.COMMAND_ID: DistributedExecuteCommand dec = (DistributedExecuteCommand) c; dec.init(cache); break; case GetInDoubtTxInfoCommand.COMMAND_ID: GetInDoubtTxInfoCommand gidTxInfoCommand = (GetInDoubtTxInfoCommand) c; gidTxInfoCommand.init(recoveryManager); break; case CompleteTransactionCommand.COMMAND_ID: CompleteTransactionCommand ccc = (CompleteTransactionCommand) c; ccc.init(recoveryManager); break; default: ModuleCommandInitializer mci = moduleCommandInitializers.get(c.getCommandId()); if (mci != null) { mci.initializeReplicableCommand(c, isRemote); } else { if (trace) log.tracef("Nothing to initialize for command: %s", c); } } }