[jgroups-dev] Advice on best practices for implementing a new protocol

Mike Jensen <[email protected]>
Newsgroups gmane.comp.java.javagroups.devel
Message-ID <[email protected]>
I started work today to implement my mesh (tentatively calling it 
"TREEMESH" but open to suggestions, I am so uncreative with names >.<).  
So far a couple (simple) questions have come up.

The first question is, how much information is too much for a header 
placed on a message?  Currently I am storing the hop record for each 
message in the header.  Which contains the JGroups address of each hop, 
and 3 shorts for each hop.  Would having a potentially largish header be 
an issue (biggest natural concern of mine would be fragmentation)?

How should properties that should be defined as final be used?  Meaning, 
I have a couple properties that should be final, because they are needed 
at init() time and after that should not be adjusted.  But I would still 
like to allow them to be configurable on some level before run time.  
Should I define those as static finals, and expect code changes for them 
to change (a user probably wont want to change them anyways)?  Or is 
there a better way to set them so they can be more flexible?  An example 
of these two values are:
    * ProcessPeriod, period of wait time for regular run processes.  I 
schedule a thread which regularly will look if it needs to establish new 
connections, timeout dead peers, and remove duplicate connections (which 
may likely occur with leaf connections due to two leaves both deciding 
at the same time they want to connect to each other, this is likely 
since they probably have the same internal models and thus both decide 
they want a connection at the same time).  This can't be adjusted 
because the value will be used at init time to schedule a reoccurring task.
    * sampleSize, this value determines how quickly we want to make 
decisions vs how sure we want to be of our selves before any decision or 
recommendations are made....with some changes this could be adjusted at 
runtime, but I think that would be a mistake

Initial discovery....I was hoping I could reuse one of the existing 
discovery protocols like TCPPING.  But I am struggling to understand 
how, and wondering if this is possible now.  Basically I was hoping the 
initial join process could look something like this
* Discovery finds a node that we can connect to
* We connect to this node and now discovery goes hands off for further 
work (meaning once we establish a connection to the first node, I don't 
want this protocol layer to try and start connecting to more nodes).
* The TreeMESH protocol then exchanges information with this node, it 
may remain connected, but most likely we will just be informed of a 
different node we should connect to instead.
* Once established in the mesh, if we are a leaf, the TreeMESH protocol 
will identify other leafs that would be good choices and connect to them too

The problem is, I don't see how other protocols become aware of 
discovery events (without the use of GMS which sends view updates, but I 
don't think will work for my needs).  Am I correct in saying that 
Event.CONNECT only represents this process joining the channel (not 
establishing a connection to another node, or another node establishing 
to us...if that is the case, how do we even know when another node has 
connected to us?)?  As far as I can tell, I will need to create my own 
discovery in order to accomplish what I described above? 

(p.s. while investigating this, I think I found a small logging bug.  I 
think line 499 (CVS head) in JChannel should be this.cluster_name...i 
was thinking this might return the wrong name if your trying to connect 
to a different channel than your already connected to)

I am not sure how to tell the transport layer to establish a connection 
directly to another node.  I assume this would be an event, but I can't 
tell what kind of event would do such a thing. 

If you have time, I would appreciate a quick look at the start of my 
main TREEMESH.java protocol (attached).  Right now it is just a template 
with lots of TODO statements.  I am sure there is much more definition 
that needs to be added in here still.  But I was hoping you guys could 
take a quick look through it and let me know if I am on the right track, 
or what recommendations you have.  My biggest concern right now is how 
to integrate in the protocol stack properly.  I am not sure what events 
I can/will get, or what events I should send.

Feel free to ask any questions you might have about the directions I 
have been taking this.  Thanks for spending some time to help me get 
some momentum to this.

Cheers

------------------------------------------------------------------------------
Sell apps to millions through the Intel(R) Atom(Tm) Developer Program
Be part of this innovative community and reach millions of netbook users 
worldwide. Take advantage of special opportunities to increase revenue and 
speed time-to-market. Join now, and jumpstart your future.
http://p.sf.net/sfu/intel-atom-d2d

_______________________________________________
Javagroups-development mailing list
TREEMESH.java (text/x-java, 6.7 KB)
package org.jgroups.protocols.jentMESH;

import org.jgroups.*;
import org.jgroups.annotations.*;
import org.jgroups.protocols.RELAY.RelayHeader;
import org.jgroups.stack.Protocol;
import org.jgroups.util.TimeScheduler;
import org.jgroups.util.Util;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Protocol to establish groups where all nodes are not connected to all other nodes.
 * See design document located at: http://archive.jentfoo.com/coding_projects/jentMesh_design.pdf
 *
 * @author Mike Jensen
 */
@Experimental @Unsupported
@MBean(description="TREEMESH protocol")
public class TREEMESH extends Protocol {
  /* ------------------------------------------    Properties     ---------------------------------------------- */
  @Property(description="How frequently in milliseconds should the node broadcast it's heartbeat")
  protected int broadcastIntravel = 1000;

  @Property(description="How long till we timeout a silent node, should be above the heartbeat")
  protected int nodeTimeout = broadcastIntravel * 2;

  @Property(description="How many connections should each peer try to maintain")
  protected short peerConMax = 3;

  @Property(description="Number representing how sure we want to be before we make decisions")
  protected final short sampleSize = 5;
  
  @Property(description="Time delay between regular process calls in milliseconds")
  protected final long processPeriod = 500;
  
  /* ---------------------------------------------    Fields    ------------------------------------------------ */
  protected Address local_addr;

  @ManagedAttribute
  protected volatile boolean is_coord = false;  // TODO - is_coord = Util.isCoordinator(view, local_addr);

  protected TimeScheduler timer;
  private MeshModel meshModel;
  
  public void init() throws Exception {
    meshModel = new MeshModel(sampleSize);
    timer = getTransport().getTimer();
    
    // schedule a thread to do regular tasks
    timer.scheduleAtFixedRate(new Runnable() {
      @Override
      public void run() {
        maybeMakePeerConnections();
        
        maybeMakeLeafConnections();
        
        removeDuplicateConnections();

        timeoutDeadNodes();
      }
    }, 500, processPeriod, TimeUnit.MILLISECONDS);
    
    // schedule a thread to send the heartbeat
    timer.scheduleAtFixedRate(new Runnable() {
      @Override
      public void run() {
        // TODO - send heartbeat
      }
    }, 500, broadcastIntravel, TimeUnit.MILLISECONDS);
  }

  protected void maybeMakePeerConnections() {
    /* TODO - if no peer connections exist, 
     *        connect to a peer recommended from the MeshModel */
  }

  protected void maybeMakeLeafConnections() {
    /* TODO - if one peer connection exists, and we have less than peerConMax - 1 leaf connections, 
     *        establish a connection to the most distant leaf */
  }

  protected void removeDuplicateConnections() {
    /* TODO - cycle through all connections, 
     *        if there is a duplicate connection and the hash of our address is lower than the remote address, 
     *        remove the connection 
     *        (if it is higher, we expect the remote peer will destroy the connection, 
     *        this way both connections are not destroyed at the same time) */
  }
  
  protected void timeoutDeadNodes() {
    /* TODO - Cycle through all known nodes, 
     *        if we have not heard any heartbeats or messages that are from or passed through that node, 
     *        assume they are dead and take appropriate action */
  }

  public void stop() {
  }
  
  public Object down(Event event) {
    switch (event.getType()) {
      case Event.MSG:
        // add our header on the message before passing on
        Message msg = (Message)event.getArg();
        msg.putHeaderIfAbsent(getId(), new TreeMeshHeader(local_addr, 
                                                          meshModel.getAvgHops(), 
                                                          meshModel.peerConnectionCount(), 
                                                          meshModel.leafConnectionCount()));
        break;
      case Event.DISCONNECT:
        break;
      case Event.SET_LOCAL_ADDRESS:
        local_addr = (Address) event.getArg();
        break;
      default: 
        log.warn("Got unhandled down event: " + event.toString());  // TODO - remove temp logging
        break;
    }
    return down_prot.down(event);
  }
  
  public Object up(Event event) {
    switch (event.getType()) {
      case Event.MSG:
          Message msg = (Message)event.getArg();
          Address dest = msg.getDest();
          TreeMeshHeader hdr = (TreeMeshHeader)msg.getHeader(getId());
          
          // analyze the message for more informatio
          meshModel.analyzeMessageHopRecord(hdr.hopRecord);
          
          // TODO - forward the msg to other connected nodes as needed
          // TODO - forward to application if needed
        break;
      default: 
        log.warn("Got unhandled up event: " + event.toString());  // TODO - remove temp logging
        break;
    }
    return up_prot.up(event);
  }
  
  public static class TreeMeshHeader extends Header {
    private List<MsgHop> hopRecord;
    
    private TreeMeshHeader() {
      hopRecord = new LinkedList<MsgHop>();
    }
    
    public TreeMeshHeader(Address local_addr, short avgHops, short peerConnections, short leafConnections) {
      this();
      addHop(local_addr, avgHops, peerConnections, leafConnections);
    }
    
    public int size() {
      // hop entries * size of entry
      return hopRecord.size() * (((Short.SIZE / 8) * 3) + 1); // TODO - this is not very accurate
    }
    
    public void writeTo(DataOutputStream out) throws IOException {
    }
    
    public void readFrom(DataInputStream in) throws IOException, IllegalAccessException,
        InstantiationException {
    }
    
    public void addHop(Address local_addr, short avgHops, short peerConnections, short leafConnections) {
      if (local_addr != null) {
        hopRecord.add(new MsgHop(local_addr, avgHops, peerConnections, leafConnections));
      } else {
        // TODO - log something scary
      }
    }
    
    public class MsgHop implements Serializable {
      public final Address host;
      public final short avgHops;
      public final short peerConnections;
      public final short leafConnections;
      
      public MsgHop(Address host, short avgHops, short peerConnections, short leafConnections) {
        this.host = host;
        this.avgHops = avgHops;
        this.peerConnections = peerConnections;
        this.leafConnections = leafConnections;
      }
    }
  }
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.