@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); }
private void updateTransactionVersion(InvocationContext context) { if (!context.isInTxScope() && !context.isOriginLocal()) { return; } if (context instanceof SingleKeyNonTxInvocationContext) { if (log.isDebugEnabled()) { log.debugf( "Received a SingleKeyNonTxInvocationContext... This should be a single read operation"); } return; } TxInvocationContext txInvocationContext = (TxInvocationContext) context; List<EntryVersion> entryVersionList = new LinkedList<EntryVersion>(); entryVersionList.add(txInvocationContext.getTransactionVersion()); if (log.isTraceEnabled()) { log.tracef( "[%s] Keys read in this command: %s", txInvocationContext.getGlobalTransaction().prettyPrint(), txInvocationContext.getKeysReadInCommand()); } for (InternalGMUCacheEntry internalGMUCacheEntry : txInvocationContext.getKeysReadInCommand().values()) { Object key = internalGMUCacheEntry.getKey(); boolean local = cll.localNodeIsOwner(key); if (log.isTraceEnabled()) { log.tracef( "[%s] Analyze entry [%s]: local?=%s", txInvocationContext.getGlobalTransaction().prettyPrint(), internalGMUCacheEntry, local); } if (txInvocationContext.hasModifications() && !internalGMUCacheEntry.isMostRecent()) { throw new CacheException("Read-Write transaction read an old value and should rollback"); } if (internalGMUCacheEntry.getMaximumTransactionVersion() != null) { entryVersionList.add(internalGMUCacheEntry.getMaximumTransactionVersion()); } txInvocationContext.getCacheTransaction().addReadKey(key); if (local) { txInvocationContext.setAlreadyReadOnThisNode(true); txInvocationContext.addReadFrom(cll.getAddress()); } } if (entryVersionList.size() > 1) { EntryVersion[] txVersionArray = new EntryVersion[entryVersionList.size()]; txInvocationContext.setTransactionVersion( versionGenerator.mergeAndMax(entryVersionList.toArray(txVersionArray))); } }
/** Special processing required for transaction commands. */ private Object handleTxCommand(TxInvocationContext ctx, TransactionBoundaryCommand command) throws Throwable { // For local commands we may not have a GlobalTransaction yet Address address = ctx.isOriginLocal() ? ctx.getOrigin() : ctx.getGlobalTransaction().getAddress(); return handleTopologyAffectedCommand(ctx, command, address, true); }
private void invalidateAcrossCluster(boolean synchronous, Object[] keys, InvocationContext ctx) throws Throwable { // increment invalidations counter if statistics maintained incrementInvalidations(); final InvalidateCommand invalidateCommand = commandsFactory.buildInvalidateCommand(InfinispanCollections.<Flag>emptySet(), keys); if (log.isDebugEnabled()) log.debug("Cache [" + rpcManager.getAddress() + "] replicating " + invalidateCommand); ReplicableCommand command = invalidateCommand; if (ctx.isInTxScope()) { TxInvocationContext txCtx = (TxInvocationContext) ctx; // A Prepare command containing the invalidation command in its 'modifications' list is sent // to the remote nodes // so that the invalidation is executed in the same transaction and locks can be acquired and // released properly. // This is 1PC on purpose, as an optimisation, even if the current TX is 2PC. // If the cache uses 2PC it's possible that the remotes will commit the invalidation and the // originator rolls back, // but this does not impact consistency and the speed benefit is worth it. command = commandsFactory.buildPrepareCommand( txCtx.getGlobalTransaction(), Collections.<WriteCommand>singletonList(invalidateCommand), true); } rpcManager.invokeRemotely(null, command, rpcManager.getDefaultRpcOptions(synchronous)); }
@Override public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command) throws Throwable { try { return invokeNextInterceptor(ctx, command); } catch (TimeoutException te) { numLocksAfterTeOnPrepare = lm1.getNumberOfLocksHeld(); isTxInTableAfterTeOnPrepare = txTable1.containRemoteTx(ctx.getGlobalTransaction()); teReceived = true; throw te; } }
@Override public final CompletableFuture<Void> visitPrepareCommand( TxInvocationContext ctx, PrepareCommand command) throws Throwable { if (ctx.isOriginLocal()) { ((VersionedPrepareCommand) command) .setVersionsSeen(ctx.getCacheTransaction().getVersionsRead()); // for local mode keys ctx.getCacheTransaction().setUpdatedEntryVersions(EMPTY_VERSION_MAP); return ctx.onReturn( (rCtx, rCommand, rv, throwable) -> { if (throwable == null && shouldCommitDuringPrepare((PrepareCommand) rCommand, ctx)) { commitContextEntries(ctx, null, null); } return null; }); } // Remote context, delivered in total order wrapEntriesForPrepare(ctx, command); return ctx.onReturn( (rCtx, rCommand, rv, throwable) -> { if (throwable != null) throw throwable; TxInvocationContext txInvocationContext = (TxInvocationContext) rCtx; VersionedPrepareCommand prepareCommand = (VersionedPrepareCommand) rCommand; EntryVersionsMap versionsMap = cdl.createNewVersionsAndCheckForWriteSkews( versionGenerator, txInvocationContext, prepareCommand); if (prepareCommand.isOnePhaseCommit()) { commitContextEntries(txInvocationContext, null, null); } else { if (trace) log.tracef( "Transaction %s will be committed in the 2nd phase", txInvocationContext.getGlobalTransaction().globalId()); } return CompletableFuture.completedFuture( versionsMap == null ? rv : new ArrayList<Object>(versionsMap.keySet())); }); }
protected Object endInvalidationAndInvokeNextInterceptor( TxInvocationContext ctx, VisitableCommand command) throws Throwable { try { if (ctx.isOriginLocal()) { // send async Commit Set<Object> affectedKeys = ctx.getAffectedKeys(); if (log.isTraceEnabled()) { log.tracef("Sending end invalidation for keys %s asynchronously", affectedKeys); } if (!affectedKeys.isEmpty()) { EndInvalidationCommand commitCommand = cacheCommandInitializer.buildEndInvalidationCommand( cacheName, affectedKeys.toArray(), ctx.getGlobalTransaction()); rpcManager.invokeRemotely( null, commitCommand, rpcManager.getDefaultRpcOptions(false, DeliverOrder.NONE)); } } } finally { return invokeNextInterceptor(ctx, command); } }