Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.infinispan.partitionhandling.impl.PartitionHandlingManagerImpl Maven / Gradle / Ivy
package org.infinispan.partitionhandling.impl;
import org.infinispan.Cache;
import org.infinispan.commands.CommandsFactory;
import org.infinispan.commands.ReplicableCommand;
import org.infinispan.commands.remote.recovery.TxCompletionNotificationCommand;
import org.infinispan.commands.tx.VersionedCommitCommand;
import org.infinispan.commands.write.WriteCommand;
import org.infinispan.commons.util.CollectionFactory;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.Configurations;
import org.infinispan.container.versioning.EntryVersionsMap;
import org.infinispan.distribution.DistributionManager;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.annotations.Start;
import org.infinispan.notifications.cachelistener.CacheNotifier;
import org.infinispan.partitionhandling.AvailabilityMode;
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.RpcManager;
import org.infinispan.remoting.transport.Address;
import org.infinispan.statetransfer.StateTransferManager;
import org.infinispan.topology.CacheTopology;
import org.infinispan.topology.LocalTopologyManager;
import org.infinispan.transaction.xa.GlobalTransaction;
import org.infinispan.util.concurrent.locks.LockManager;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import java.util.*;
public class PartitionHandlingManagerImpl implements PartitionHandlingManager {
private static final Log log = LogFactory.getLog(PartitionHandlingManagerImpl.class);
private static final boolean trace = log.isTraceEnabled();
private final Map partialTransactions;
private volatile AvailabilityMode availabilityMode = AvailabilityMode.AVAILABLE;
private DistributionManager distributionManager;
private LocalTopologyManager localTopologyManager;
private StateTransferManager stateTransferManager;
private String cacheName;
private CacheNotifier notifier;
private CommandsFactory commandsFactory;
private Configuration configuration;
private RpcManager rpcManager;
private LockManager lockManager;
private boolean isVersioned;
public PartitionHandlingManagerImpl() {
partialTransactions = CollectionFactory.makeConcurrentMap();
}
@Inject
public void init(DistributionManager distributionManager, LocalTopologyManager localTopologyManager,
StateTransferManager stateTransferManager, Cache cache, CacheNotifier notifier, CommandsFactory commandsFactory,
Configuration configuration, RpcManager rpcManager, LockManager lockManager) {
this.distributionManager = distributionManager;
this.localTopologyManager = localTopologyManager;
this.stateTransferManager = stateTransferManager;
this.cacheName = cache.getName();
this.notifier = notifier;
this.commandsFactory = commandsFactory;
this.configuration = configuration;
this.rpcManager = rpcManager;
this.lockManager = lockManager;
}
@Start
public void start() {
isVersioned = Configurations.isVersioningEnabled(configuration);
}
@Override
public AvailabilityMode getAvailabilityMode() {
return availabilityMode;
}
@Override
public void setAvailabilityMode(AvailabilityMode availabilityMode) {
if (availabilityMode != this.availabilityMode) {
log.debugf("Updating availability for cache %s: %s -> %s", cacheName, this.availabilityMode, availabilityMode);
notifier.notifyPartitionStatusChanged(availabilityMode, true);
this.availabilityMode = availabilityMode;
notifier.notifyPartitionStatusChanged(availabilityMode, false);
}
}
@Override
public void checkWrite(Object key) {
doCheck(key);
}
@Override
public void checkRead(Object key) {
doCheck(key);
}
@Override
public void checkClear() {
if (availabilityMode != AvailabilityMode.AVAILABLE) {
throw log.clearDisallowedWhilePartitioned();
}
}
@Override
public void checkBulkRead() {
if (availabilityMode != AvailabilityMode.AVAILABLE) {
throw log.partitionDegraded();
}
}
@Override
public CacheTopology getLastStableTopology() {
return localTopologyManager.getStableCacheTopology(cacheName);
}
@Override
public boolean addPartialRollbackTransaction(GlobalTransaction globalTransaction, Collection affectedNodes, Collection lockedKeys) {
if (trace) {
log.tracef("Added partially rollback transaction %s", globalTransaction);
}
partialTransactions.put(globalTransaction, new RollbackTransactionInfo(globalTransaction, affectedNodes, lockedKeys));
return true;
}
@Override
public boolean addPartialCommit2PCTransaction(GlobalTransaction globalTransaction, Collection affectedNodes,
Collection lockedKeys, EntryVersionsMap newVersions) {
if (trace) {
log.tracef("Added partially committed (2PC) transaction %s", globalTransaction);
}
partialTransactions.put(globalTransaction, new Commit2PCTransactionInfo(globalTransaction, affectedNodes,
lockedKeys, newVersions));
return true;
}
@Override
public boolean addPartialCommit1PCTransaction(GlobalTransaction globalTransaction, Collection affectedNodes,
Collection lockedKeys, List modifications) {
if (trace) {
log.tracef("Added partially committed (1PC) transaction %s", globalTransaction);
}
partialTransactions.put(globalTransaction, new Commit1PCTransactionInfo(globalTransaction, affectedNodes,
lockedKeys, modifications));
return true;
}
@Override
public boolean isTransactionPartiallyCommitted(GlobalTransaction globalTransaction) {
TransactionInfo transactionInfo = partialTransactions.get(globalTransaction);
// If we are going to commit, we can't release the resources yet
boolean partiallyCommitted = transactionInfo != null && !transactionInfo.isRolledBack();
if (trace) {
log.tracef("Can release resources for transaction %s? %s. Transaction info=%s", globalTransaction,
!partiallyCommitted, transactionInfo);
}
return partiallyCommitted;
}
@Override
public Collection getPartialTransactions() {
return Collections.unmodifiableCollection(partialTransactions.keySet());
}
@Override
public boolean canRollbackTransactionAfterOriginatorLeave(GlobalTransaction globalTransaction) {
boolean canRollback = availabilityMode == AvailabilityMode.AVAILABLE &&
!getLastStableTopology().getActualMembers().contains(globalTransaction.getAddress());
if (trace) {
log.tracef("Can rollback transaction? %s", canRollback);
}
return canRollback;
}
@Override
public void onTopologyUpdate(CacheTopology cacheTopology) {
boolean isStable = isTopologyStable(cacheTopology);
if (isStable) {
if (trace) {
log.tracef("On stable topology update. Pending txs: %d", partialTransactions.size());
}
for (TransactionInfo transactionInfo : partialTransactions.values()) {
completeTransaction(transactionInfo, cacheTopology);
}
}
}
private void completeTransaction(final TransactionInfo transactionInfo, CacheTopology cacheTopology) {
rpcManager.invokeRemotelyAsync(transactionInfo.getCommitNodes(cacheTopology),
transactionInfo.buildCommand(commandsFactory, isVersioned),
rpcManager.getDefaultRpcOptions(true))
.whenComplete((responseMap, throwable) -> {
final GlobalTransaction globalTransaction = transactionInfo.getGlobalTransaction();
if (throwable != null) {
if (trace) {
log.tracef(throwable, "Exception for transaction %s. Retry later.", globalTransaction);
}
return;
}
if (trace) {
log.tracef("Future done for transaction %s. Response are %s", globalTransaction, responseMap);
}
for (Response response : responseMap.values()) {
if (response == UnsureResponse.INSTANCE || response == CacheNotFoundResponse.INSTANCE) {
if (trace) {
log.tracef("Another partition or topology changed for transaction %s. Retry later.", globalTransaction);
}
return;
}
}
if (trace) {
log.tracef("Performing cleanup for transaction %s", globalTransaction);
}
lockManager.unlock(transactionInfo.getLockedKeys(), globalTransaction);
partialTransactions.remove(globalTransaction);
TxCompletionNotificationCommand command = commandsFactory.buildTxCompletionNotificationCommand(null, globalTransaction);
//a little bit overkill, but the state transfer can happen during a merge and some nodes can receive the
// transaction that aren't in the original affected nodes.
//no side effects.
rpcManager.invokeRemotely(null, command, rpcManager.getDefaultRpcOptions(false, DeliverOrder.NONE));
});
}
private boolean isTopologyStable(CacheTopology cacheTopology) {
CacheTopology stableTopology = localTopologyManager.getStableCacheTopology(cacheName);
if (trace) {
log.tracef("Check if topology %s is stable. Last stable topology is %s", cacheTopology, stableTopology);
}
return stableTopology != null && cacheTopology.getActualMembers().containsAll(stableTopology.getActualMembers());
}
private void doCheck(Object key) {
if (trace) log.tracef("Checking availability for key=%s, status=%s", key, availabilityMode);
if (availabilityMode == AvailabilityMode.AVAILABLE)
return;
List owners = distributionManager.locate(key);
List actualMembers = stateTransferManager.getCacheTopology().getActualMembers();
if (!actualMembers.containsAll(owners)) {
if (trace) log.tracef("Partition is in %s mode, access is not allowed for key %s", availabilityMode, key);
throw log.degradedModeKeyUnavailable(key);
} else {
if (trace) log.tracef("Key %s is available.", key);
}
}
private interface TransactionInfo {
boolean isRolledBack();
List getCommitNodes(CacheTopology stableTopology);
ReplicableCommand buildCommand(CommandsFactory commandsFactory, boolean isVersioned);
GlobalTransaction getGlobalTransaction();
Collection getLockedKeys();
}
private static class RollbackTransactionInfo extends BaseTransactionInfo {
protected RollbackTransactionInfo(GlobalTransaction globalTransaction, Collection affectedNodes, Collection lockedKeys) {
super(globalTransaction, affectedNodes, lockedKeys);
}
@Override
public boolean isRolledBack() {
return true;
}
@Override
public ReplicableCommand buildCommand(CommandsFactory commandsFactory, boolean isVersioned) {
return commandsFactory.buildRollbackCommand(getGlobalTransaction());
}
}
private static class Commit2PCTransactionInfo extends BaseTransactionInfo {
private final EntryVersionsMap newVersions;
public Commit2PCTransactionInfo(GlobalTransaction globalTransaction, Collection affectedNodes, Collection lockedKeys, EntryVersionsMap newVersions) {
super(globalTransaction, affectedNodes, lockedKeys);
this.newVersions = newVersions;
}
@Override
public boolean isRolledBack() {
return false;
}
@Override
public ReplicableCommand buildCommand(CommandsFactory commandsFactory, boolean isVersioned) {
if (isVersioned) {
VersionedCommitCommand commitCommand = commandsFactory.buildVersionedCommitCommand(getGlobalTransaction());
commitCommand.setUpdatedVersions(newVersions);
return commitCommand;
} else {
return commandsFactory.buildCommitCommand(getGlobalTransaction());
}
}
}
private static class Commit1PCTransactionInfo extends BaseTransactionInfo {
private final List modifications;
public Commit1PCTransactionInfo(GlobalTransaction globalTransaction, Collection affectedNodes, Collection lockedKeys, List modifications) {
super(globalTransaction, affectedNodes, lockedKeys);
this.modifications = modifications;
}
@Override
public boolean isRolledBack() {
return false;
}
@Override
public ReplicableCommand buildCommand(CommandsFactory commandsFactory, boolean isVersioned) {
if (isVersioned) {
throw new IllegalArgumentException("Cannot build a versioned one-phase-commit prepare command.");
}
return commandsFactory.buildPrepareCommand(getGlobalTransaction(), modifications, true);
}
}
private static abstract class BaseTransactionInfo implements TransactionInfo {
private final GlobalTransaction globalTransaction;
private final Collection affectedNodes;
private final Collection lockedKeys;
protected BaseTransactionInfo(GlobalTransaction globalTransaction, Collection affectedNodes, Collection lockedKeys) {
this.globalTransaction = globalTransaction;
this.lockedKeys = lockedKeys;
this.affectedNodes = affectedNodes;
}
@Override
public final List getCommitNodes(CacheTopology stableTopology) {
if (affectedNodes == null) {
return null;
} else {
List commitNodes = new ArrayList<>(affectedNodes);
commitNodes.retainAll(stableTopology.getActualMembers());
return commitNodes;
}
}
@Override
public final GlobalTransaction getGlobalTransaction() {
return globalTransaction;
}
@Override
public Collection getLockedKeys() {
return lockedKeys;
}
@Override
public String toString() {
return "TransactionInfo{" +
"globalTransaction=" + globalTransaction + ", " +
"rollback=" + isRolledBack() + ", " +
"affectedNodes=" + affectedNodes +
'}';
}
}
}