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

org.jgroups.protocols.MERGE2 Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and Jakarta Messaging 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: 35.0.0.Beta1
Show newest version
package org.jgroups.protocols;


import org.jgroups.*;
import org.jgroups.annotations.*;
import org.jgroups.stack.Protocol;
import org.jgroups.util.*;

import java.io.DataInput;
import java.io.DataOutput;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


/**
 * Protocol to discover subgroups; e.g., existing due to a network partition (that healed). Example: group
 * {p,q,r,s,t,u,v,w} is split into 3 subgroups {p,q}, {r,s,t,u} and {v,w}. This protocol will eventually send
 * a MERGE event with the coordinators of each subgroup up the stack: {p,r,v}. Note that - depending on the time
 * of subgroup discovery - there could also be 2 MERGE events, which first join 2 of the subgroups, and then the
 * resulting group to the last subgroup. The real work of merging the subgroups into one larger group is done
 * somewhere above this protocol (typically in the GMS protocol).

* This protocol works as follows: *

    *
  • If coordinator: periodically multicast a discovery message. Everyone (or (if configured) only the coords) reply * with their current view. Based on the received views: *
  • If there is more than 1 coordinator: *
      *
    1. Get all coordinators *
    2. Create a MERGE event with the list of coordinators as argument *
    3. Send the event up the stack *
    *
* *

* * Provides: sends MERGE event with list of coordinators up the stack
* @author Bela Ban, Oct 16 2001 * @deprecated Use {@link org.jgroups.protocols.MERGE3} instead, as it causes less traffic, especially for large clusters */ @MBean(description="Protocol to discover subgroups existing due to a network partition") @Deprecated public class MERGE2 extends Protocol { /* ----------------------------------------- Properties -------------------------------------------------- */ @Property(description="Minimum time in ms between runs to discover other clusters") protected long min_interval=5000; @Property(description="Maximum time in ms between runs to discover other clusters") protected long max_interval=20000; @Property(description="Number of inconsistent views with only 1 coord after a MERGE event is sent up") protected int inconsistent_view_threshold=1; @Property(description="When receiving a multicast message, checks if the sender is member of the cluster. " + "If not, initiates a merge. Generates a lot of traffic for large clusters when there is a lot of merging") protected boolean merge_fast=true; @Property(description="The delay (in milliseconds) after which a merge fast execution is started") protected long merge_fast_delay=1000; @Property(description="Always sends a discovery response, no matter what",writable=true) protected boolean force_sending_discovery_rsps=true; @Property(description="Time (in ms) to wait for all discovery responses") protected long discovery_timeout=5000; /* ---------------------------------------------- JMX -------------------------------------------------------- */ @ManagedAttribute(writable=false, description="whether or not a merge task is currently running " + "(should be the case in a coordinator") public boolean isMergeTaskRunning() { return task.isRunning(); } /* --------------------------------------------- Fields ------------------------------------------------------ */ protected Address local_addr; protected volatile View view; protected final Set

members=new HashSet<>(); protected final Set
merge_candidates=new CopyOnWriteArraySet<>(); protected final FindSubgroupsTask task=new FindSubgroupsTask(); @ManagedAttribute(description="Whether this member is the current coordinator") protected volatile boolean is_coord=false; protected volatile Address current_coord; protected TimeScheduler timer; @ManagedAttribute(description="Number of inconsistent 1-coord views until a MERGE event is sent up the stack") protected int num_inconsistent_views; @ManagedAttribute(description="Number of times a MERGE event was sent up the stack") protected int num_merge_events; protected final Map views=new ConcurrentHashMap<>(); protected final Lock discovery_lock=new ReentrantLock(); protected final CondVar discovery_cond=new CondVar(discovery_lock); protected volatile boolean fetching_done=false; protected boolean transport_supports_multicasting=true; protected String cluster_name; public void init() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer cannot be retrieved"); if(min_interval <= 0 || max_interval <= 0) throw new Exception("min_interval and max_interval have to be > 0"); if(max_interval <= min_interval) throw new Exception ("max_interval has to be greater than min_interval"); transport_supports_multicasting=getTransport().supportsMulticasting(); } public long getMinInterval() { return min_interval; } public void setMinInterval(long i) { min_interval=i; } public long getMaxInterval() { return max_interval; } public void setMaxInterval(long l) { max_interval=l; } protected boolean isMergeRunning() { Object retval=up_prot.up(new Event(Event.IS_MERGE_IN_PROGRESS)); return retval instanceof Boolean && (Boolean)retval; } /** Discovers members and detects whether we have multiple coordinator. If so, kicks off a merge */ @ManagedOperation public void sendMergeSolicitation() { task.findAndNotify(); } @ManagedAttribute(description="The address of the current coordinator") public String getCurrentCoord() {return current_coord != null? current_coord.toString() : "n/a";} @ManagedOperation public void startMergeTask() {task.start();} @ManagedOperation public void stopMergeTask() {task.stop();} @ManagedOperation(description="Fetches all views") public String fetchAllViews() { try { task.fetchViews(); StringBuilder sb=new StringBuilder(); for(Map.Entry entry: views.entrySet()) sb.append(entry.getKey() + ": " + entry.getValue() + "\n"); return sb.toString(); } catch(Throwable e) { return null; } } public void stop() { is_coord=false; merge_candidates.clear(); task.stop(); } public Object down(Event evt) { switch(evt.getType()) { case Event.CONNECT: case Event.CONNECT_USE_FLUSH: case Event.CONNECT_WITH_STATE_TRANSFER: case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH: cluster_name=(String)evt.getArg(); return down_prot.down(evt); case Event.VIEW_CHANGE: Object ret=down_prot.down(evt); view=(View)evt.getArg(); List
mbrs=view.getMembers(); if(mbrs == null || mbrs.isEmpty() || local_addr == null) { task.stop(); return ret; } members.clear(); members.addAll(mbrs); merge_candidates.removeAll(members); current_coord=mbrs.isEmpty()? null : mbrs.get(0); if(current_coord != null && current_coord.equals(local_addr)) { is_coord=true; task.start(); // start task if we became coordinator (doesn't start if already running) } else { // if we were coordinator, but are no longer, stop task. this happens e.g. when we merge and someone // else becomes the new coordinator of the merged group is_coord=false; task.stop(); } return ret; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); return down_prot.down(evt); default: return down_prot.down(evt); // Pass on to the layer below us } } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); MergeHeader hdr=(MergeHeader)msg.getHeader(id); if(hdr == null) { if(merge_fast) mergeFast(msg.dest(), msg.src()); break; } else { handle(hdr, msg.src()); return null; } } return up_prot.up(evt); } public void up(MessageBatch batch) { for(Message msg: batch) { MergeHeader hdr=(MergeHeader)msg.getHeader(id); if(hdr != null) { batch.remove(msg); handle(hdr, batch.sender()); } } if(!batch.isEmpty()) { if(merge_fast) mergeFast(batch.dest(), batch.sender()); up_prot.up(batch); } } protected void mergeFast(final Address dest, final Address sender) { if(dest == null) { if(!members.contains(sender) && merge_candidates.add(sender)) { timer.schedule(new Runnable() { public void run() { if(!members.contains(sender)) task.findAndNotify(); } }, merge_fast_delay, TimeUnit.MILLISECONDS); } } } protected void handle(MergeHeader hdr, Address sender) { switch(hdr.type) { case 1: // request boolean send_discovery_rsp=force_sending_discovery_rsps || is_coord || current_coord == null || current_coord.equals(sender); if(!send_discovery_rsp) { log.trace("%s: suppressing discovery response as I'm not a coordinator and the " + "discovery request was not sent by a coordinator", local_addr); return; } if(isMergeRunning()) { log.trace("%s: suppressing discovery response as a merge is in progress", local_addr); return; } if(view != null) { Message msg=new Message(sender).putHeader(id,new MergeHeader(MergeHeader.RSP,view)); down_prot.down(new Event(Event.MSG, msg)); } break; case 2: // response View rsp_view=hdr.view; if(rsp_view != null && !fetching_done) { views.put(sender,rsp_view); List diff_views=Util.detectDifferentViews(views); if(diff_views.size() > 1) { discovery_lock.lock(); try { fetching_done=true; discovery_cond.signal(true); } finally { discovery_lock.unlock(); } } } break; } } /** * Task periodically executing (if role is coordinator). Gets the initial membership and determines * whether there are subgroups (multiple coordinators for the same group). If yes, it sends a MERGE event * with the list of the coordinators up the stack */ protected class FindSubgroupsTask implements Condition { @GuardedBy("this") protected Future future; protected final Lock lock=new ReentrantLock(); public synchronized void start() { if(future == null || future.isDone()) { future=timer.scheduleWithDynamicInterval(new TimeScheduler.Task() { public long nextInterval() { return computeInterval(); } public void run() { findAndNotify(); } public String toString() { return MERGE2.class.getSimpleName() + ": " + getClass().getSimpleName(); } }); } } public synchronized void stop() { if(future != null) { future.cancel(true); future=null; } } public synchronized boolean isRunning() { return future != null && !future.isDone(); } public boolean isMet() {return fetching_done;} public void findAndNotify() { if(isMergeRunning()) return; if(lock.tryLock()) { try { _findAndNotify(); } catch(InterruptedException iex) { } catch(Throwable t) { log.error("FindSubgroupsTask failed", t); } finally { lock.unlock(); } } } protected void _findAndNotify() throws InterruptedException { fetchViews(); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append(local_addr + ": discovery results:\n"); for(Map.Entry entry: views.entrySet()) sb.append("[" + entry.getKey() + "]: " + entry.getValue().getViewId()).append("\n"); log.trace(sb); } // A list of different views List different_views=Util.detectDifferentViews(views); if(different_views.size() <= 1) { num_inconsistent_views=0; return; } Collection
merge_participants=Util.determineMergeParticipants(views); if(merge_participants.size() == 1) { if(num_inconsistent_views < inconsistent_view_threshold) { log.debug("%s: dropping MERGE for inconsistent views (%s) as inconsistent view threshold (%d) " + "has not yet been reached (%d)", local_addr, Util.printViews(different_views), inconsistent_view_threshold, num_inconsistent_views); num_inconsistent_views++; return; } else num_inconsistent_views=0; } else num_inconsistent_views=0; if(log.isDebugEnabled()) { StringBuilder sb=new StringBuilder(); sb.append(local_addr + " found different views : " + Util.printViews(different_views) + "; sending up MERGE event with merge participants " + merge_participants + ".\n"); sb.append("Discovery results:\n"); for(Map.Entry entry: views.entrySet()) sb.append("[" + entry.getKey() + "]: coord=" + entry.getValue().getCreator()).append("\n"); log.debug(sb.toString()); } Event evt=new Event(Event.MERGE, views); try { up_prot.up(evt); num_merge_events++; } catch(Throwable t) { log.error("failed sending up MERGE event", t); } } /** Fetches views from members in the cluster. Returns when either discovery_timeout ms have elapsed or * more than 1 different view has been found */ protected void fetchViews() throws InterruptedException { views.clear(); View tmp_view=view; if(tmp_view != null) views.put(local_addr, tmp_view); fetching_done=false; if(transport_supports_multicasting) { Message discovery_req=new Message(null).putHeader(id, new MergeHeader(MergeHeader.REQ, null)) .setTransientFlag(Message.TransientFlag.DONT_LOOPBACK); down_prot.down(new Event(Event.MSG, discovery_req)); try { discovery_cond.waitFor(this, discovery_timeout, TimeUnit.MILLISECONDS); } finally { fetching_done=true; } return; } Responses rsps=(Responses)down_prot.down(Event.FIND_MBRS_EVT); rsps.waitFor(discovery_timeout); // return immediately if done if(rsps.isEmpty()) return; log.trace("discovery protocol returned %d responses: %s", rsps.size(), rsps); for(PingData rsp: rsps) { PhysicalAddress addr=rsp.getPhysicalAddr(); if(addr == null) continue; Message discovery_req=new Message(addr).putHeader(id, new MergeHeader(MergeHeader.REQ, null)) .setTransientFlag(Message.TransientFlag.DONT_LOOPBACK); down_prot.down(new Event(Event.MSG, discovery_req)); } try { discovery_cond.waitFor(this, discovery_timeout, TimeUnit.MILLISECONDS); } finally { fetching_done=true; } } /** * Returns a random value within [min_interval - max_interval] */ protected long computeInterval() { return min_interval + Util.random(max_interval - min_interval); } } protected static class MergeHeader extends Header { protected byte type=1; // 1 == req, 2 == rsp protected View view; protected static final byte REQ=1; protected static final byte RSP=2; public MergeHeader() { } public MergeHeader(byte type, View view) { this.type=type; this.view=view; } public String toString() { return (type==1? "req" : "rsp") + ", view=" + view; } public int size() { return Global.BYTE_SIZE + Util.size(view); } public void writeTo(DataOutput out) throws Exception { out.writeByte(type); Util.writeView(view, out); } public void readFrom(DataInput in) throws Exception { type=in.readByte(); view=Util.readView(in); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy