
org.infinispan.interceptors.distribution.TxDistributionInterceptor Maven / Gradle / Ivy
package org.infinispan.interceptors.distribution;
import org.infinispan.commands.FlagAffectedCommand;
import org.infinispan.commands.control.LockControlCommand;
import org.infinispan.commands.read.AbstractDataCommand;
import org.infinispan.commands.read.GetCacheEntryCommand;
import org.infinispan.commands.read.GetKeyValueCommand;
import org.infinispan.commands.tx.CommitCommand;
import org.infinispan.commands.tx.PrepareCommand;
import org.infinispan.commands.tx.RollbackCommand;
import org.infinispan.commands.tx.TransactionBoundaryCommand;
import org.infinispan.commands.tx.VersionedCommitCommand;
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.commands.write.ValueMatcher;
import org.infinispan.commands.write.WriteCommand;
import org.infinispan.configuration.cache.Configurations;
import org.infinispan.container.EntryFactory;
import org.infinispan.container.entries.CacheEntry;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.container.versioning.EntryVersionsMap;
import org.infinispan.context.Flag;
import org.infinispan.context.InvocationContext;
import org.infinispan.context.impl.LocalTxInvocationContext;
import org.infinispan.context.impl.TxInvocationContext;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.annotations.Start;
import org.infinispan.partitionhandling.impl.PartitionHandlingManager;
import org.infinispan.remoting.inboundhandler.DeliverOrder;
import org.infinispan.remoting.responses.CacheNotFoundResponse;
import org.infinispan.remoting.responses.Response;
import org.infinispan.remoting.responses.UnsureResponse;
import org.infinispan.remoting.rpc.ResponseMode;
import org.infinispan.remoting.rpc.RpcOptions;
import org.infinispan.remoting.transport.Address;
import org.infinispan.statetransfer.OutdatedTopologyException;
import org.infinispan.transaction.LockingMode;
import org.infinispan.transaction.impl.LocalTransaction;
import org.infinispan.transaction.xa.GlobalTransaction;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static java.lang.String.format;
import static org.infinispan.util.DeltaCompositeKeyUtil.filterDeltaCompositeKeys;
import static org.infinispan.util.DeltaCompositeKeyUtil.getAffectedKeysFromContext;
/**
* Handles the distribution of the transactional caches.
*
* @author Mircea Markus
* @deprecated Since 8.2, no longer public API.
*/
@Deprecated
public class TxDistributionInterceptor extends BaseDistributionInterceptor {
private static Log log = LogFactory.getLog(TxDistributionInterceptor.class);
private static final boolean trace = log.isTraceEnabled();
private PartitionHandlingManager partitionHandlingManager;
private boolean isPessimisticCache;
private boolean useClusteredWriteSkewCheck;
@Inject
public void inject(PartitionHandlingManager partitionHandlingManager) {
this.partitionHandlingManager = partitionHandlingManager;
}
@Start
public void start() {
isPessimisticCache = cacheConfiguration.transaction().lockingMode() == LockingMode.PESSIMISTIC;
useClusteredWriteSkewCheck = Configurations.isVersioningEnabled(cacheConfiguration);
}
@Override
public Object visitReplaceCommand(InvocationContext ctx, ReplaceCommand command) throws Throwable {
try {
return handleTxWriteCommand(ctx, command, command.getKey());
} finally {
if (ctx.isOriginLocal()) {
// If the state transfer interceptor has to retry the command, it should ignore the previous value.
command.setValueMatcher(command.isSuccessful() ? ValueMatcher.MATCH_ALWAYS : ValueMatcher.MATCH_NEVER);
}
}
}
@Override
public Object visitRemoveCommand(InvocationContext ctx, RemoveCommand command) throws Throwable {
try {
return handleTxWriteCommand(ctx, command, command.getKey());
} finally {
if (ctx.isOriginLocal()) {
// If the state transfer interceptor has to retry the command, it should ignore the previous value.
command.setValueMatcher(command.isSuccessful() ? ValueMatcher.MATCH_ALWAYS : ValueMatcher.MATCH_NEVER);
}
}
}
@Override
public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable {
if (command.hasFlag(Flag.PUT_FOR_EXTERNAL_READ)) {
return handleNonTxWriteCommand(ctx, command);
}
Object returnValue = handleTxWriteCommand(ctx, command, command.getKey());
if (ctx.isOriginLocal()) {
// If the state transfer interceptor has to retry the command, it should ignore the previous value.
command.setValueMatcher(command.isSuccessful() ? ValueMatcher.MATCH_ALWAYS : ValueMatcher.MATCH_NEVER);
}
return returnValue;
}
@Override
public Object visitPutMapCommand(InvocationContext ctx, PutMapCommand command) throws Throwable {
// don't bother with a remote get for the PutMapCommand!
return invokeNextInterceptor(ctx, command);
}
@Override
public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable {
return visitGetCommand(ctx, command);
}
@Override
public Object visitGetCacheEntryCommand(InvocationContext ctx, GetCacheEntryCommand command) throws Throwable {
return visitGetCommand(ctx, command);
}
private Object visitGetCommand(InvocationContext ctx, AbstractDataCommand command) throws Throwable {
Object key = command.getKey();
CacheEntry entry = ctx.lookupEntry(key);
// If the cache entry has the value lock flag set, skip the remote get.
if (ctx.isOriginLocal() && valueIsMissing(entry)) {
InternalCacheEntry remoteEntry = null;
if (readNeedsRemoteValue(ctx, command)) {
remoteEntry = remoteGet(ctx, key, false, command);
}
if (remoteEntry == null) {
localGet(ctx, key, false);
}
}
return invokeNextInterceptor(ctx, command);
}
@Override
public Object visitLockControlCommand(TxInvocationContext ctx, LockControlCommand command) throws Throwable {
if (ctx.isOriginLocal()) {
//In Pessimistic mode, the delta composite keys were sent to the wrong owner and never locked.
final Collection affectedNodes = cdl.getOwners(filterDeltaCompositeKeys(command.getKeys()));
((LocalTxInvocationContext) ctx).remoteLocksAcquired(affectedNodes == null ? dm.getConsistentHash()
.getMembers() : affectedNodes);
log.tracef("Registered remote locks acquired %s", affectedNodes);
RpcOptions rpcOptions = rpcManager.getRpcOptionsBuilder(ResponseMode.SYNCHRONOUS_IGNORE_LEAVERS, DeliverOrder.NONE).build();
Map responseMap = rpcManager.invokeRemotely(affectedNodes, command, rpcOptions);
checkTxCommandResponses(responseMap, command, (LocalTxInvocationContext) ctx,
((LocalTxInvocationContext) ctx).getRemoteLocksAcquired());
}
return invokeNextInterceptor(ctx, command);
}
// ---- TX boundary commands
@Override
public Object visitCommitCommand(TxInvocationContext ctx, CommitCommand command) throws Throwable {
if (shouldInvokeRemoteTxCommand(ctx)) {
Collection recipients = getCommitNodes(ctx);
Map responseMap =
rpcManager.invokeRemotely(recipients, command, createCommitRpcOptions());
checkTxCommandResponses(responseMap, command, (LocalTxInvocationContext) ctx, recipients);
}
return invokeNextInterceptor(ctx, command);
}
@Override
public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command) throws Throwable {
Object retVal = invokeNextInterceptor(ctx, command);
if (shouldInvokeRemoteTxCommand(ctx)) {
Collection recipients = cdl.getOwners(getAffectedKeysFromContext(ctx));
prepareOnAffectedNodes(ctx, command, recipients);
((LocalTxInvocationContext) ctx).remoteLocksAcquired(
recipients == null ? dm.getWriteConsistentHash().getMembers() : recipients);
}
return retVal;
}
protected void prepareOnAffectedNodes(TxInvocationContext> ctx, PrepareCommand command, Collection recipients) {
try {
// this method will return immediately if we're the only member (because exclude_self=true)
Map responseMap = rpcManager.invokeRemotely(recipients, command, createPrepareRpcOptions());
checkTxCommandResponses(responseMap, command, (LocalTxInvocationContext) ctx, recipients);
} finally {
transactionRemotelyPrepared(ctx);
}
}
@Override
public Object visitRollbackCommand(TxInvocationContext ctx, RollbackCommand command) throws Throwable {
if (shouldInvokeRemoteTxCommand(ctx)) {
Collection recipients = getCommitNodes(ctx);
Map responseMap = rpcManager.invokeRemotely(recipients, command, createRollbackRpcOptions());
checkTxCommandResponses(responseMap, command, (LocalTxInvocationContext) ctx, recipients);
}
return invokeNextInterceptor(ctx, command);
}
private Collection getCommitNodes(TxInvocationContext ctx) {
LocalTransaction localTx = (LocalTransaction) ctx.getCacheTransaction();
Collection affectedNodes = cdl.getOwners(getAffectedKeysFromContext(ctx));
List members = dm.getConsistentHash().getMembers();
return localTx.getCommitNodes(affectedNodes, rpcManager.getTopologyId(), members);
}
protected void checkTxCommandResponses(Map responseMap, TransactionBoundaryCommand command,
LocalTxInvocationContext context, Collection recipients) {
OutdatedTopologyException outdatedTopologyException = null;
for (Map.Entry e : responseMap.entrySet()) {
Address recipient = e.getKey();
Response response = e.getValue();
if (response == CacheNotFoundResponse.INSTANCE) {
// No need to retry if the missing node wasn't a member when the command started.
if (command.getTopologyId() == stateTransferManager.getCacheTopology().getTopologyId()
&& !rpcManager.getMembers().contains(recipient)) {
if (trace) log.tracef("Ignoring response from node not targeted %s", recipient);
} else {
if (checkCacheNotFoundResponseInPartitionHandling(command, context, recipients)) {
if (trace) log.tracef("Cache not running on node %s, or the node is missing. It will be handled by the PartitionHandlingManager", recipient);
return;
} else {
if (trace) log.tracef("Cache not running on node %s, or the node is missing", recipient);
//noinspection ThrowableInstanceNeverThrown
outdatedTopologyException = new OutdatedTopologyException(format("Cache not running on node %s, or the node is missing", recipient));
}
}
} else if (response == UnsureResponse.INSTANCE) {
if (trace) log.tracef("Node %s has a newer topology id", recipient);
//noinspection ThrowableInstanceNeverThrown
outdatedTopologyException = new OutdatedTopologyException(format("Node %s has a newer topology id", recipient));
}
}
if (outdatedTopologyException != null) {
throw outdatedTopologyException;
}
}
private boolean checkCacheNotFoundResponseInPartitionHandling(TransactionBoundaryCommand command,
LocalTxInvocationContext context,
Collection recipients) {
final GlobalTransaction globalTransaction = command.getGlobalTransaction();
final Collection
© 2015 - 2025 Weber Informatics LLC | Privacy Policy