package org.infinispan.interceptors.distribution; import java.util.concurrent.Future; import org.infinispan.commands.FlagAffectedCommand; import org.infinispan.commands.tx.CommitCommand; import org.infinispan.commands.tx.PrepareCommand; import org.infinispan.commands.write.PutKeyValueCommand; import org.infinispan.commands.write.PutMapCommand; import org.infinispan.commands.write.RemoveCommand; import org.infinispan.commands.write.ReplaceCommand; import org.infinispan.context.InvocationContext; import org.infinispan.context.impl.TxInvocationContext; import org.infinispan.remoting.transport.jgroups.SuspectException; /** * Interceptor that handles L1 logic for transactional caches. * * @author William Burns */ public class L1TxInterceptor extends L1NonTxInterceptor { @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { return performCommandWithL1WriteIfAble(ctx, command, false, true, true); } @Override public Object visitPutMapCommand(InvocationContext ctx, PutMapCommand command) throws Throwable { // TODO: need to figure out if we do anything here? - is the prepare/commmit L1 invalidation sufficient? return invokeNext(ctx, command); } @Override public Object visitReplaceCommand(InvocationContext ctx, ReplaceCommand command) throws Throwable { return performCommandWithL1WriteIfAble(ctx, command, false, true, true); } @Override public Object visitRemoveCommand(InvocationContext ctx, RemoveCommand command) throws Throwable { return performCommandWithL1WriteIfAble(ctx, command, false, true, false); } @Override public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command) throws Throwable { if (command.isOnePhaseCommit() && shouldFlushL1(ctx)) { blockOnL1FutureIfNeeded(flushL1Caches(ctx)); } return invokeNext(ctx, command); } @Override public Object visitCommitCommand(TxInvocationContext ctx, CommitCommand command) throws Throwable { if (shouldFlushL1(ctx)) { blockOnL1FutureIfNeeded(flushL1Caches(ctx)); } return invokeNext(ctx, command); } @Override protected boolean skipL1Lookup(FlagAffectedCommand command, Object key) { // TODO: need to skip L1 lookups when the command doesn't require the value to be returned like unsafe return values or write skew check ?? return super.skipL1Lookup(command, key); } private boolean shouldFlushL1(TxInvocationContext ctx) { return !ctx.getAffectedKeys().isEmpty(); } private Future<?> flushL1Caches(TxInvocationContext ctx) { return l1Manager.flushCache(ctx.getAffectedKeys(), ctx.getOrigin(), true); } private void blockOnL1FutureIfNeeded(Future<?> f) { if (f != null) { try { f.get(); } catch (Exception e) { // Ignore SuspectExceptions - if the node has gone away then there is nothing to invalidate anyway. if (!(e.getCause() instanceof SuspectException)) { getLog().failedInvalidatingRemoteCache(e); } } } } }