@Override public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); boolean sync = isSynchronous(ctx); if (shouldInvokeRemoteTxCommand(ctx)) { int newCacheViewId = -1; stateTransferLock.waitForStateTransferToEnd(ctx, command, newCacheViewId); if (command.isOnePhaseCommit()) flushL1Caches(ctx); // if we are one-phase, don't block on this future. Collection<Address> recipients = dm.getAffectedNodes(ctx.getAffectedKeys()); prepareOnAffectedNodes(ctx, command, recipients, sync); ((LocalTxInvocationContext) ctx).remoteLocksAcquired(recipients); } else if (isL1CacheEnabled && command.isOnePhaseCommit() && !ctx.isOriginLocal() && !ctx.getLockedKeys().isEmpty()) { // We fall into this block if we are a remote node, happen to be the primary data owner and // have locked keys. // it is still our responsibility to invalidate L1 caches in the cluster. flushL1Caches(ctx); } return retVal; }
private void sendCommitCommand( TxInvocationContext ctx, CommitCommand command, Collection<Address> preparedOn) throws TimeoutException, InterruptedException { // we only send the commit command to the nodes that Collection<Address> recipients = dm.getAffectedNodes(ctx.getAffectedKeys()); // By default, use the configured commit sync settings boolean syncCommitPhase = configuration.isSyncCommitPhase(); for (Address a : preparedOn) { if (!recipients.contains(a)) { // However if we have prepared on some nodes and are now committing on different nodes, make // sure we // force sync commit so we can respond to prepare resend requests. syncCommitPhase = true; } } Map<Address, Response> responses = rpcManager.invokeRemotely(recipients, command, syncCommitPhase, true); if (!responses.isEmpty()) { List<Address> resendTo = new LinkedList<Address>(); for (Map.Entry<Address, Response> r : responses.entrySet()) { if (needToResendPrepare(r.getValue())) resendTo.add(r.getKey()); } if (!resendTo.isEmpty()) { log.debugf( "Need to resend prepares for %s to %s", command.getGlobalTransaction(), resendTo); PrepareCommand pc = buildPrepareCommandForResend(ctx, command); rpcManager.invokeRemotely(resendTo, pc, true, true); } } }
@Override protected void prepareOnAffectedNodes( TxInvocationContext<?> ctx, PrepareCommand command, Collection<Address> recipients) { if (log.isTraceEnabled()) { log.tracef( "Total Order Anycast transaction %s with Total Order", command.getGlobalTransaction().globalId()); } if (!ctx.hasModifications()) { return; } if (!ctx.isOriginLocal()) { throw new IllegalStateException("Expected a local context while TO-Anycast prepare command"); } if (!(command instanceof VersionedPrepareCommand)) { throw new IllegalStateException( "Expected a Versioned Prepare Command in version aware component"); } try { KeysValidateFilter responseFilter = ctx.getCacheTransaction().hasModification(ClearCommand.class) || isSyncCommitPhase() ? null : new KeysValidateFilter(rpcManager.getAddress(), ctx.getAffectedKeys()); totalOrderPrepare(recipients, command, responseFilter); } finally { transactionRemotelyPrepared(ctx); } }
private Object realRemoteGet( InvocationContext ctx, Object key, boolean storeInL1, boolean isWrite) throws Throwable { if (trace) log.tracef("Doing a remote get for key %s", key); boolean acquireRemoteLock = false; if (ctx.isInTxScope()) { TxInvocationContext txContext = (TxInvocationContext) ctx; acquireRemoteLock = isWrite && isPessimisticCache && !txContext.getAffectedKeys().contains(key); } // attempt a remote lookup InternalCacheEntry ice = dm.retrieveFromRemoteSource(key, ctx, acquireRemoteLock); if (acquireRemoteLock) { ((TxInvocationContext) ctx).addAffectedKey(key); } if (ice != null) { if (storeInL1) { if (isL1CacheEnabled) { if (trace) log.tracef("Caching remotely retrieved entry for key %s in L1", key); // This should be fail-safe try { long lifespan = ice.getLifespan() < 0 ? configuration.getL1Lifespan() : Math.min(ice.getLifespan(), configuration.getL1Lifespan()); PutKeyValueCommand put = cf.buildPutKeyValueCommand( ice.getKey(), ice.getValue(), lifespan, -1, ctx.getFlags()); lockAndWrap(ctx, key, ice); invokeNextInterceptor(ctx, put); } catch (Exception e) { // Couldn't store in L1 for some reason. But don't fail the transaction! log.infof("Unable to store entry %s in L1 cache", key); log.debug("Inability to store in L1 caused by", e); } } else { CacheEntry ce = ctx.lookupEntry(key); if (ce == null || ce.isNull() || ce.isLockPlaceholder() || ce.getValue() == null) { if (ce != null && ce.isChanged()) { ce.setValue(ice.getValue()); } else { if (isWrite) lockAndWrap(ctx, key, ice); else ctx.putLookedUpEntry(key, ice); } } } } else { if (trace) log.tracef("Not caching remotely retrieved entry for key %s in L1", key); } return ice.getValue(); } return null; }
@Override public Object visitRollbackCommand(TxInvocationContext ctx, RollbackCommand command) throws Throwable { if (shouldInvokeRemoteTxCommand(ctx)) { rpcManager.invokeRemotely( dm.getAffectedNodes(ctx.getAffectedKeys()), command, configuration.isSyncRollbackPhase(), true); } return invokeNextInterceptor(ctx, command); }
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); } }