All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgroups.protocols.tom.TOA Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
package org.jgroups.protocols.tom;

import java.util.Collection;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import org.jgroups.Address;
import org.jgroups.AnycastAddress;
import org.jgroups.Event;
import org.jgroups.Message;
import org.jgroups.View;
import org.jgroups.annotations.MBean;
import org.jgroups.annotations.ManagedAttribute;
import org.jgroups.annotations.ManagedOperation;
import org.jgroups.stack.Protocol;

/**
 * Total Order Anycast with three communication steps (based on Skeen's Algorithm). Establishes total order for a
 * message sent to a subset of the cluster members (an anycast). Example: send a totally ordered message to {D,E}
 * out of a membership of {A,B,C,D,E,F}.

* Skeen's algorithm uses consensus among the anycast target members to find the currently highest * sequence number (seqno) and delivers the message according to the order established by the seqnos. * * @author Pedro Ruivo * @since 3.1 */ @MBean(description = "Implementation of Total Order Anycast based on Skeen's Algorithm") public class TOA extends Protocol implements DeliveryProtocol { //managers private DeliveryManagerImpl deliverManager; private SenderManager senderManager; // threads private volatile DeliveryThread deliverThread = new DeliveryThread(this); //local address private Address localAddress; //sequence numbers, messages ids and lock private final AtomicLong messageIdCounter = new AtomicLong(0); //stats: profiling information private final StatsCollector statsCollector = new StatsCollector(); public TOA() { } @Override public void start() throws Exception { deliverManager = new DeliveryManagerImpl(); senderManager = new SenderManager(); if(deliverThread == null) { deliverThread=new DeliveryThread(this); deliverThread.setLocalAddress(localAddress.toString()); } deliverThread.start(deliverManager); statsCollector.setStatsEnabled(statsEnabled()); } @Override public void stop() { deliverThread.interrupt(); deliverThread=null; } @Override public Object down(Event evt) { switch (evt.getType()) { case Event.SET_LOCAL_ADDRESS: this.localAddress =evt.getArg(); if(this.deliverThread != null) this.deliverThread.setLocalAddress(localAddress.toString()); break; case Event.VIEW_CHANGE: handleViewChange(evt.getArg()); break; default: break; } return down_prot.down(evt); } public Object down(Message message) { Address dest = message.getDest(); if (dest != null && dest instanceof AnycastAddress && !message.isFlagSet(Message.Flag.NO_TOTAL_ORDER)) { // anycast message sendTotalOrderAnycastMessage(extract((AnycastAddress) dest), message); } else if (dest != null && dest instanceof AnycastAddress) { //anycast address with NO_TOTAL_ORDER flag (should no be possible, but...) send(extract((AnycastAddress) dest), message, true); } else { //normal message down_prot.down(message); } return null; } @Override public Object up(Event evt) { switch (evt.getType()) { case Event.VIEW_CHANGE: handleViewChange(evt.getArg()); break; case Event.SET_LOCAL_ADDRESS: this.localAddress =evt.getArg(); this.deliverThread.setLocalAddress(localAddress.toString()); break; default: break; } return up_prot.up(evt); } public Object up(Message message) { ToaHeader header=message.getHeader(this.id); if (header == null) return up_prot.up(message); switch (header.getType()) { case ToaHeader.DATA_MESSAGE: handleDataMessage(message, header); break; case ToaHeader.PROPOSE_MESSAGE: handleSequenceNumberPropose(message.getSrc(), header); break; case ToaHeader.FINAL_MESSAGE: handleFinalSequenceNumber(header); break; case ToaHeader.SINGLE_DESTINATION_MESSAGE: if (log.isTraceEnabled()) { log.trace("Received message %s with SINGLE_DESTINATION header. delivering...", message); } deliverManager.deliverSingleDestinationMessage(message, header.getMessageID()); break; default: throw new IllegalStateException("Unknown header type received " + header); } return null; } @Override public void deliver(Message message) { message.setDest(localAddress); if (log.isTraceEnabled()) { log.trace("Deliver message %s (%s) in total order", message, message.getHeader(id)); } up_prot.up(message); statsCollector.incrementMessageDeliver(); } private void handleViewChange(View view) { if (log.isTraceEnabled()) { log.trace("Handle view %s", view); } final Collection

leavers = deliverManager.handleView(view); //basis behavior: drop leavers message (as senders) //basis behavior: avoid waiting for the acks Collection pendingSentMessages = senderManager.getPendingMessageIDs(); for (MessageID messageID : pendingSentMessages) { long finalSequenceNumber = senderManager.removeLeavers(messageID, leavers); if (finalSequenceNumber != SenderManager.NOT_READY) { ToaHeader finalHeader = ToaHeader.newFinalMessageHeader(messageID, finalSequenceNumber); Message finalMessage = new Message().src(localAddress).putHeader(this.id, finalHeader) .setFlag(Message.Flag.OOB, Message.Flag.INTERNAL, Message.Flag.DONT_BUNDLE); if (log.isTraceEnabled()) { log.trace("Message %s is ready to be delivered. Final sequencer number is %d", messageID, finalSequenceNumber); } send(senderManager.getDestination(messageID), finalMessage, false); //returns true if we are in destination set if (senderManager.markSent(messageID)) { deliverManager.markReadyToDeliver(messageID, finalSequenceNumber); } } } // TODO: Future work: How to add fault tolerance? (simple and efficient) } private void sendTotalOrderAnycastMessage(Collection
destinations, Message message) { boolean trace = log.isTraceEnabled(); long startTime = statsCollector.now(); final boolean deliverToMySelf = destinations.contains(localAddress); final MessageID messageID = generateId(); if (destinations.size() == 1) { message.putHeader(id, ToaHeader.createSingleDestinationHeader(messageID)); message.setDest(destinations.iterator().next()); if (trace) { log.trace("Sending total order anycast message %s (%s) to single destination", message, message.getHeader(id)); } if (deliverToMySelf) { deliverManager.deliverSingleDestinationMessage(message, messageID); } else { down_prot.down(message); } return; } try { ToaHeader header = ToaHeader.newDataMessageHeader(messageID, deliverManager.getViewId()); message.putHeader(this.id, header); //sets the sequence number in header! long sequenceNumber = deliverToMySelf ? deliverManager.addLocalMessageToDeliver(messageID, message, header) : -1; if (trace) { log.trace("Sending total order anycast message %s (%s) to %s", message, message.getHeader(id), destinations); } senderManager.addNewMessageToSend(messageID, destinations, sequenceNumber, deliverToMySelf); send(destinations, message, false); } catch (Exception e) { logException("Exception caught while sending anycast message. Error is " + e.getLocalizedMessage(), e); } finally { long duration = statsCollector.now() - startTime; statsCollector.addAnycastSentDuration(duration, (destinations.size() - (deliverToMySelf ? 1 : 0))); } } private MessageID generateId() { return new MessageID(localAddress, messageIdCounter.getAndIncrement()); } private void send(Collection
destinations, Message msg, boolean sendToMyself) { if (log.isTraceEnabled()) { log.trace("sending anycast total order message %s to %s", msg, destinations); } for (Address address : destinations) { if (!sendToMyself && address.equals(localAddress)) { continue; } Message cpy = msg.copy(); cpy.setDest(address); down_prot.down(cpy); } } private void handleDataMessage(Message message, ToaHeader header) { final long startTime = statsCollector.now(); try { final MessageID messageID = header.getMessageID(); //create the sequence number and put it in deliver manager long myProposeSequenceNumber = deliverManager.addRemoteMessageToDeliver(messageID, message, header.getSequencerNumber(), header.getViewId()); if (log.isTraceEnabled()) { log.trace("Received the message with %s. The proposed sequence number is %d", header, myProposeSequenceNumber); } if (myProposeSequenceNumber == -1) { //message discarded. not sending ack back. return; } //create a new message and send it back ToaHeader newHeader = ToaHeader.newProposeMessageHeader(messageID, myProposeSequenceNumber); Message proposeMessage = new Message().src(localAddress).dest(messageID.getAddress()) .putHeader(this.id, newHeader).setFlag(Message.Flag.OOB, Message.Flag.INTERNAL, Message.Flag.DONT_BUNDLE); //multicastSenderThread.addUnicastMessage(proposeMessage); down_prot.down(proposeMessage); } catch (Exception e) { logException("Exception caught while processing the data message " + header.getMessageID(), e); } finally { statsCollector.addDataMessageDuration(statsCollector.now() - startTime); } } private void handleSequenceNumberPropose(Address from, ToaHeader header) { long startTime = statsCollector.now(); long duration = -1; boolean lastProposeReceived = false; boolean trace = log.isTraceEnabled(); try { MessageID messageID = header.getMessageID(); if (trace) { log.trace("Received the proposed sequence number message with %s from %s", header, from); } deliverManager.updateSequenceNumber(header.getSequencerNumber()); long finalSequenceNumber = senderManager.addPropose(messageID, from, header.getSequencerNumber()); if (finalSequenceNumber != SenderManager.NOT_READY) { lastProposeReceived = true; ToaHeader finalHeader = ToaHeader.newFinalMessageHeader(messageID, finalSequenceNumber); Message finalMessage = new Message().src(localAddress).putHeader(this.id, finalHeader) .setFlag(Message.Flag.OOB, Message.Flag.INTERNAL, Message.Flag.DONT_BUNDLE); Set
destinations = senderManager.getDestination(messageID); if (destinations.contains(localAddress)) { destinations.remove(localAddress); } if (trace) { log.trace("Message %s is ready to be delivered. Final sequencer number is %d", messageID, finalSequenceNumber); } send(destinations, finalMessage, false); //returns true if we are in destination set if (senderManager.markSent(messageID)) { deliverManager.markReadyToDeliver(messageID, finalSequenceNumber); } } duration = statsCollector.now() - startTime; } catch (Exception e) { logException("Exception caught while processing the propose sequence number for " + header.getMessageID(), e); } finally { statsCollector.addProposeSequenceNumberDuration(duration, lastProposeReceived); } } private void handleFinalSequenceNumber(ToaHeader header) { long startTime = statsCollector.now(); long duration = -1; try { MessageID messageID = header.getMessageID(); if (log.isTraceEnabled()) { log.trace("Received the final sequence number message with %s", header); } deliverManager.markReadyToDeliver(messageID, header.getSequencerNumber()); duration = statsCollector.now() - startTime; } catch (Exception e) { logException("Exception caught while processing the final sequence number for " + header.getMessageID(), e); } finally { statsCollector.addFinalSequenceNumberDuration(duration); } } private void logException(String msg, Exception e) { if (log.isDebugEnabled()) { log.debug(msg, e); } else if (log.isWarnEnabled()) { log.warn("%s. Error is %s", msg, e.getLocalizedMessage()); } } private Collection
extract(AnycastAddress anycastAddress) { return anycastAddress.findAddresses().orElseGet(deliverManager::getViewMembers); } @ManagedOperation public String getMessageList() { return deliverManager.getMessageSet().toString(); } public DeliveryManager getDeliverManager() { return deliverManager; } @Override public void enableStats(boolean flag) { super.enableStats(flag); statsCollector.setStatsEnabled(flag); } @Override public void resetStats() { super.resetStats(); statsCollector.clearStats(); } @ManagedAttribute(description = "The average duration (in milliseconds) in processing and sending the anycast " + "message to all the recipients") public double getAvgToaSendDuration() { return statsCollector.getAvgAnycastSentDuration(); } @ManagedAttribute(description = "The average duration (in milliseconds) in processing a data message received") public double getAvgDataMessageReceivedDuration() { return statsCollector.getAvgDataMessageReceivedDuration(); } @ManagedAttribute(description = "The average duration (in milliseconds) in processing a propose message received" + "(not the last one") public double getAvgProposeMessageReceivedDuration() { return statsCollector.getAvgProposeMesageReceivedDuration(); } @ManagedAttribute(description = "The average duration (in milliseconds) in processing the last propose message " + "received. This last propose message will originate the sending of the final message") public double getAvgLastProposeMessageReceivedDuration() { return statsCollector.getAvgLastProposeMessageReceivedDuration(); } @ManagedAttribute(description = "The average duration (in milliseconds) in processing a final message received") public double getAvgFinalMessageReceivedDuration() { return statsCollector.getAvgFinalMessageReceivedDuration(); } @ManagedAttribute(description = "The number of anycast messages sent") public int getNumberOfAnycastMessagesSent() { return statsCollector.getNumberOfAnycastMessagesSent(); } @ManagedAttribute(description = "The number of final anycast sent") public int getNumberOfFinalAnycastSent() { return statsCollector.getNumberOfFinalAnycastsSent(); } @ManagedAttribute(description = "The number of anycast messages delivered") public int getNumberOfAnycastMessagesDelivered() { return statsCollector.getAnycastDelivered(); } @ManagedAttribute(description = "The number of propose messages sent") public int getNumberOfProposeMessageSent() { return statsCollector.getNumberOfProposeMessagesSent(); } @ManagedAttribute(description = "The number of final messages delivered") public int getNumberOfFinalMessagesDelivered() { return statsCollector.getNumberOfFinalMessagesDelivered(); } @ManagedAttribute(description = "The number of data messages delivered") public int getNumberOfDataMessagesDelivered() { return statsCollector.getNumberOfProposeMessagesSent(); } @ManagedAttribute(description = "The number of propose messages received") public int getNumberOfProposeMessageReceived() { return statsCollector.getNumberOfProposeMessagesReceived(); } @ManagedAttribute(description = "The average number of unicasts messages created per anycast message") public double getAvgNumberOfUnicastSentPerAnycast() { return statsCollector.getAvgNumberOfUnicastSentPerAnycast(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy