package lsr.paxos.recovery; import static lsr.common.ProcessDescriptor.processDescriptor; import java.io.IOException; import java.util.BitSet; import lsr.common.SingleThreadDispatcher; import lsr.paxos.ActiveRetransmitter; import lsr.paxos.RetransmittedMessage; import lsr.paxos.SnapshotProvider; import lsr.paxos.core.Paxos; import lsr.paxos.messages.Message; import lsr.paxos.messages.MessageType; import lsr.paxos.messages.Recovery; import lsr.paxos.messages.RecoveryAnswer; import lsr.paxos.network.MessageHandler; import lsr.paxos.network.Network; import lsr.paxos.storage.SingleNumberWriter; import lsr.paxos.storage.Storage; import lsr.paxos.storage.SynchronousViewStorage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ViewSSRecovery extends RecoveryAlgorithm implements Runnable { private boolean firstRun; private Paxos paxos; private final int numReplicas; private Storage storage; private SingleThreadDispatcher dispatcher; private ActiveRetransmitter retransmitter; private RetransmittedMessage recoveryRetransmitter; public ViewSSRecovery(SnapshotProvider snapshotProvider, String stableStoragePath) throws IOException { numReplicas = processDescriptor.numReplicas; storage = createStorage(new SingleNumberWriter(stableStoragePath, "sync.view")); paxos = createPaxos(snapshotProvider, storage); dispatcher = paxos.getDispatcher(); } public Paxos getPaxos() { return paxos; } public void start() { dispatcher.submit(this); } public void run() { // do not execute recovery mechanism on first run if (firstRun) { onRecoveryFinished(); return; } retransmitter = new ActiveRetransmitter(paxos.getNetwork(), "ViewSSRecoveryRetransmitter"); retransmitter.init(); logger.info("Sending recovery message"); Network.addMessageListener(MessageType.RecoveryAnswer, new RecoveryAnswerListener()); Recovery recovery = new Recovery(storage.getView(), -1); logger.info(processDescriptor.logMark_Benchmark, "Sending {}", recovery); recoveryRetransmitter = retransmitter.startTransmitting(recovery); } protected Paxos createPaxos(SnapshotProvider snapshotProvider, Storage storage) throws IOException { return new Paxos(snapshotProvider, storage); } private Storage createStorage(SingleNumberWriter writer) { Storage storage = new SynchronousViewStorage(writer); firstRun = storage.getView() == 0; if (processDescriptor.isLocalProcessLeader(storage.getView())) { storage.setView(storage.getView() + 1); } return storage; } // Get all instances before <code>nextId</code> private void startCatchup(final int nextId) { new RecoveryCatchUp(paxos.getCatchup(), storage).recover(nextId, new Runnable() { public void run() { onRecoveryFinished(); } }); } private void onRecoveryFinished() { fireRecoveryFinished(); Network.addMessageListener(MessageType.Recovery, new ViewRecoveryRequestHandler(paxos)); } private class RecoveryAnswerListener implements MessageHandler { private BitSet received; private RecoveryAnswer answerFromLeader = null; public RecoveryAnswerListener() { received = new BitSet(numReplicas); } public void onMessageReceived(Message msg, final int sender) { assert msg.getType() == MessageType.RecoveryAnswer; final RecoveryAnswer recoveryAnswer = (RecoveryAnswer) msg; // drop messages from lower views if (recoveryAnswer.getView() < storage.getView()) { return; } logger.debug(processDescriptor.logMark_Benchmark, "Received {}", msg); if (logger.isInfoEnabled()) logger.info( "Got a recovery answer {}{}", recoveryAnswer + (processDescriptor.getLeaderOfView(recoveryAnswer.getView()) == sender ? " from leader" : "")); dispatcher.submit(new Runnable() { public void run() { if (recoveryRetransmitter == null) return; recoveryRetransmitter.stop(sender); received.set(sender); // update view if (storage.getView() < recoveryAnswer.getView()) { storage.setView(recoveryAnswer.getView()); answerFromLeader = null; } if (processDescriptor.getLeaderOfView(storage.getView()) == sender) { answerFromLeader = recoveryAnswer; } if (received.cardinality() > numReplicas / 2) { onCardinality(); } } }); } private void onCardinality() { recoveryRetransmitter.stop(); recoveryRetransmitter = null; if (answerFromLeader == null) { Recovery recovery = new Recovery(storage.getView(), -1); recoveryRetransmitter = retransmitter.startTransmitting(recovery); } else { startCatchup((int) answerFromLeader.getNextId()); Network.removeMessageListener(MessageType.RecoveryAnswer, this); } } public void onMessageSent(Message message, BitSet destinations) { } } private static final Logger logger = LoggerFactory.getLogger(ViewSSRecovery.class); }