/* * 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.commands.write.WriteCommand; import org.infinispan.interceptors.PassiveReplicationInterceptor; import org.infinispan.interceptors.base.CommandInterceptor; import org.infinispan.reconfigurableprotocol.ReconfigurableProtocol; import org.infinispan.remoting.transport.Address; import org.infinispan.transaction.LocalTransaction; import org.infinispan.transaction.xa.GlobalTransaction; import java.util.EnumMap; import static org.infinispan.interceptors.InterceptorChain.InterceptorType; /** * Represents the switch protocol when Passive Replication is in use * * @author Pedro Ruivo * @since 5.2 */ public class PassiveReplicationCommitProtocol extends ReconfigurableProtocol { public static final String UID = "PB"; private static final String MASTER_ACK = "_MASTER_ACK_"; private static final String SWITCH_TO_MASTER_ACK = "_MASTER_ACK_2_"; private static final String TWO_PC_UID = TwoPhaseCommitProtocol.UID; private boolean masterAckReceived = false; @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.unsafeSwitch(protocol); if (isCoordinator()) { new SendMasterAckThread().start(); } } @Override public final void stopProtocol(boolean abortOnStop) throws InterruptedException { if (isCoordinator()) { if (log.isDebugEnabled()) { log.debugf("[%s] Stop protocol in master for Passive Replication protocol. Wait until all local transactions " + "are finished", Thread.currentThread().getName()); } awaitUntilLocalExecutingTransactionsFinished(abortOnStop); awaitUntilLocalCommittingTransactionsFinished(); broadcastData(MASTER_ACK, false); if (log.isDebugEnabled()) { log.debugf("[%s] Ack sent to the slaves. Starting new epoch", Thread.currentThread().getName()); } } else { if (log.isDebugEnabled()) { log.debugf("[%s] Stop protocol in slave for Passive Replication protocol. Wait for the master ack", Thread.currentThread().getName()); } synchronized (this) { while (!masterAckReceived) { this.wait(); } masterAckReceived = false; } if (log.isDebugEnabled()) { log.debugf("[%s] Ack received from master. Starting new epoch", Thread.currentThread().getName()); } } //this wait should return immediately, because we don't have any remote transactions pending... //it is just to be safe awaitUntilRemoteTransactionsFinished(); } @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) { logProcessOldTransaction(globalTransaction, currentProtocol); if (TWO_PC_UID.equals(currentProtocol.getUniqueProtocolName())) { return; } throwOldTxException(globalTransaction); } @Override public final void processSpeculativeTransaction(GlobalTransaction globalTransaction, WriteCommand[] writeSet, ReconfigurableProtocol oldProtocol) { logProcessSpeculativeTransaction(globalTransaction, oldProtocol); if (TWO_PC_UID.equals(oldProtocol.getUniqueProtocolName())) { return; } throwSpeculativeTxException(globalTransaction); } @Override public final void bootstrapProtocol() { //no-op } @Override public final EnumMap<InterceptorType, CommandInterceptor> buildInterceptorChain() { EnumMap<InterceptorType, CommandInterceptor> interceptors = buildDefaultInterceptorChain(); //Custom interceptor after TxInterceptor interceptors.put(InterceptorType.CUSTOM_INTERCEPTOR_AFTER_TX_INTERCEPTOR, createInterceptor(new PassiveReplicationInterceptor(), PassiveReplicationInterceptor.class)); if (log.isTraceEnabled()) { log.tracef("Building interceptor chain for Passive Replication protocol %s", interceptors); } return interceptors; } @Override public final boolean use1PC(LocalTransaction localTransaction) { //force 1 phase commit for passive replication //return true; //original condition return !configuration.versioning().enabled() || configuration.transaction().useSynchronization(); } @Override public final boolean useTotalOrder() { return false; } @Override protected final void internalHandleData(Object data, Address from) { if (MASTER_ACK.equals(data)) { if (log.isDebugEnabled()) { log.debugf("Handle Master Ack message"); } synchronized (this) { masterAckReceived = true; this.notifyAll(); } } else if (SWITCH_TO_MASTER_ACK.equals(data)) { if (log.isDebugEnabled()) { log.debugf("Handle Switch To Master Ack message"); } try { //just to be safe... we will not have remote transactions awaitUntilRemoteTransactionsFinished(); } catch (InterruptedException e) { //ignore } manager.safeSwitch(null); } } /** * Asynchronously sends the Ack after all local transaction has finished */ private class SendMasterAckThread extends Thread { private SendMasterAckThread() { super("PB-Send-Ack-Thread"); } @Override public void run() { try { awaitUntilLocalCommittingTransactionsFinished(); } catch (InterruptedException e) { //interrupted return; } broadcastData(SWITCH_TO_MASTER_ACK, false); manager.safeSwitch(null); } } }