/*
* INESC-ID, Instituto de Engenharia de Sistemas e Computadores Investigação e Desevolvimento em Lisboa
* Copyright 2013 INESC-ID and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3.0 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.reconfigurableprotocol.protocol;
import org.infinispan.CacheException;
import org.infinispan.commands.write.WriteCommand;
import org.infinispan.interceptors.base.CommandInterceptor;
import org.infinispan.interceptors.gmu.TotalOrderGMUDistributionInterceptor;
import org.infinispan.interceptors.gmu.TotalOrderGMUEntryWrappingInterceptor;
import org.infinispan.interceptors.gmu.TotalOrderGMUReplicationInterceptor;
import org.infinispan.interceptors.totalorder.TotalOrderDistributionInterceptor;
import org.infinispan.interceptors.totalorder.TotalOrderInterceptor;
import org.infinispan.interceptors.totalorder.TotalOrderReplicationInterceptor;
import org.infinispan.interceptors.totalorder.TotalOrderStateTransferLockInterceptor;
import org.infinispan.interceptors.totalorder.TotalOrderVersionedDistributionInterceptor;
import org.infinispan.interceptors.totalorder.TotalOrderVersionedEntryWrappingInterceptor;
import org.infinispan.interceptors.totalorder.TotalOrderVersionedReplicationInterceptor;
import org.infinispan.reconfigurableprotocol.ReconfigurableProtocol;
import org.infinispan.remoting.transport.Address;
import org.infinispan.transaction.LocalTransaction;
import org.infinispan.transaction.TransactionTable;
import org.infinispan.transaction.xa.GlobalTransaction;
import org.infinispan.util.concurrent.IsolationLevel;
import java.util.EnumMap;
import static org.infinispan.interceptors.InterceptorChain.InterceptorType;
/**
* Represents the switch protocol when Total Order based replication is in use
*
* @author Pedro Ruivo
* @since 5.2
*/
public class TotalOrderCommitProtocol extends ReconfigurableProtocol {
public static final String UID = "TO";
private static final String TWO_PC_UID = TwoPhaseCommitProtocol.UID;
private TransactionTable transactionTable;
@Override
public final String getUniqueProtocolName() {
return UID;
}
@Override
public final boolean canSwitchTo(ReconfigurableProtocol protocol) {
return TWO_PC_UID.equals(protocol.getUniqueProtocolName());
}
@Override
public final void switchTo(ReconfigurableProtocol protocol) {
manager.safeSwitch(protocol);
}
@Override
public final void stopProtocol(boolean abortOnStop) throws InterruptedException {
globalStopProtocol(true, abortOnStop);
}
@Override
public final void bootProtocol() {
//no-op
}
@Override
public final void processTransaction(GlobalTransaction globalTransaction, WriteCommand[] writeSet) {
logProcessTransaction(globalTransaction);
}
@Override
public final void processOldTransaction(GlobalTransaction globalTransaction, WriteCommand[] writeSet,
ReconfigurableProtocol currentProtocol) {
try {
throwOldTxException(globalTransaction);
} catch (CacheException ce) {
addException(ce, globalTransaction);
throw ce;
}
}
@Override
public final void processSpeculativeTransaction(GlobalTransaction globalTransaction, WriteCommand[] writeSet,
ReconfigurableProtocol oldProtocol) {
try{
logProcessSpeculativeTransaction(globalTransaction, oldProtocol);
if (TWO_PC_UID.equals(oldProtocol.getUniqueProtocolName())) {
try {
oldProtocol.ensureNoConflict(writeSet);
return;
} catch (InterruptedException e) {
//no-op
}
}
throwSpeculativeTxException(globalTransaction);
} catch (CacheException ce) {
addException(ce, globalTransaction);
throw ce;
} catch (Exception e) {
addException(e, globalTransaction);
throw new CacheException(e);
}
}
@Override
public final void bootstrapProtocol() {
transactionTable = getComponent(TransactionTable.class);
}
@Override
public final EnumMap<InterceptorType, CommandInterceptor> buildInterceptorChain() {
EnumMap<InterceptorType, CommandInterceptor> interceptors = buildDefaultInterceptorChain();
//State transfer
interceptors.put(InterceptorType.STATE_TRANSFER,
createInterceptor(new TotalOrderStateTransferLockInterceptor(),
TotalOrderStateTransferLockInterceptor.class));
//Custom interceptor
interceptors.put(InterceptorType.CUSTOM_INTERCEPTOR_BEFORE_TX_INTERCEPTOR,
createInterceptor(new TotalOrderInterceptor(), TotalOrderInterceptor.class));
//No locking
interceptors.remove(InterceptorType.LOCKING);
//Wrapper
if (configuration.locking().isolationLevel() == IsolationLevel.SERIALIZABLE) {
interceptors.put(InterceptorType.WRAPPER,
createInterceptor(new TotalOrderGMUEntryWrappingInterceptor(),
TotalOrderGMUEntryWrappingInterceptor.class));
} else if (needsVersionAwareComponents()) {
interceptors.put(InterceptorType.WRAPPER,
createInterceptor(new TotalOrderVersionedEntryWrappingInterceptor(),
TotalOrderVersionedEntryWrappingInterceptor.class));
}
//No deadlock
interceptors.remove(InterceptorType.DEADLOCK);
//Clustering
switch (configuration.clustering().cacheMode()) {
case REPL_SYNC:
if (configuration.locking().isolationLevel() == IsolationLevel.SERIALIZABLE) {
interceptors.put(InterceptorType.CLUSTER,
createInterceptor(new TotalOrderGMUReplicationInterceptor(),
TotalOrderGMUReplicationInterceptor.class));
} else if (needsVersionAwareComponents()) {
interceptors.put(InterceptorType.CLUSTER,
createInterceptor(new TotalOrderVersionedReplicationInterceptor(),
TotalOrderVersionedReplicationInterceptor.class));
} else {
interceptors.put(InterceptorType.CLUSTER,
createInterceptor(new TotalOrderReplicationInterceptor(),
TotalOrderReplicationInterceptor.class));
}
break;
case DIST_SYNC:
if (configuration.locking().isolationLevel() == IsolationLevel.SERIALIZABLE) {
interceptors.put(InterceptorType.CLUSTER,
createInterceptor(new TotalOrderGMUDistributionInterceptor(),
TotalOrderGMUDistributionInterceptor.class));
} else if (needsVersionAwareComponents()) {
interceptors.put(InterceptorType.CLUSTER,
createInterceptor(new TotalOrderVersionedDistributionInterceptor(),
TotalOrderVersionedDistributionInterceptor.class));
} else {
interceptors.put(InterceptorType.CLUSTER,
createInterceptor(new TotalOrderDistributionInterceptor(),
TotalOrderDistributionInterceptor.class));
}
break;
}
if (log.isTraceEnabled()) {
log.tracef("Building interceptor chain for Total Order protocol %s", interceptors);
}
return interceptors;
}
@Override
public final boolean use1PC(LocalTransaction localTransaction) {
//force always one phase commit for total order protocol
//return true;
//original condition:
if (configuration.locking().isolationLevel() == IsolationLevel.SERIALIZABLE) {
return false;
} else if (!configuration.versioning().enabled()) {
return true;
}
return configuration.transaction().useSynchronization() && !configuration.clustering().cacheMode().isDistributed();
}
@Override
public final boolean useTotalOrder() {
return true;
}
@Override
protected final void internalHandleData(Object data, Address from) {
//no-op
}
private void addException(Exception e, GlobalTransaction globalTransaction) {
//this is not the most efficient way to do it, but it should have a lower number of local transactions
for (LocalTransaction localTransaction : transactionTable.getLocalTransactions()) {
if (localTransaction.getGlobalTransaction().equals(globalTransaction)) {
//TODO revisit this!
//localTransaction.addException(e, true);
break;
}
}
}
}