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

org.jgroups.protocols.pbcast.CoordGmsImpl Maven / Gradle / Ivy

There is a newer version: 9.1.7.Final
Show newest version

package org.jgroups.protocols.pbcast;


import org.jgroups.Address;
import org.jgroups.Event;
import org.jgroups.Message;
import org.jgroups.View;
import org.jgroups.util.*;

import java.util.*;


/**
 * Coordinator role of the Group MemberShip (GMS) protocol. Accepts JOIN and LEAVE requests and emits view changes
 * accordingly.
 * @author Bela Ban
 */
public class CoordGmsImpl extends ServerGmsImpl {
    protected static final Long  MAX_SUSPEND_TIMEOUT=30000L;

    public CoordGmsImpl(GMS g) {
        super(g);
    }


    public MergeId getMergeId() {
        return merger.getMergeId();
    }


    public void init() throws Exception {
        super.init();
        merger.cancelMerge(null);
    }

    public void join(Address mbr,boolean useFlushIfPresent) {
        wrongMethod("join");
    }
    
    public void joinWithStateTransfer(Address mbr,boolean useFlushIfPresent) {
        wrongMethod("join");
    }

    /** The coordinator itself wants to leave the group */
    public void leave(Address mbr) {
        if(mbr == null) {
            if(log.isErrorEnabled()) log.error("member's address is null !");
            return;
        }
        if(mbr.equals(gms.local_addr))
            leaving=true;
        gms.getViewHandler().add(new Request(Request.LEAVE, mbr, false));
        gms.getViewHandler().stop(true); // wait until all requests have been processed, then close the queue and leave

        // If we're the coord leaving, ignore gms.leave_timeout: https://issues.jboss.org/browse/JGRP-1509
        long timeout=(long)(Math.max(gms.leave_timeout, gms.view_ack_collection_timeout) * 1.10);
        gms.getViewHandler().waitUntilCompleted(timeout);
    }


    public void suspect(Address mbr) {
        if(mbr.equals(gms.local_addr)) {
            if(log.isWarnEnabled()) log.warn("I am the coord and I'm suspected -- will probably leave shortly");
            return;
        }        
        Collection suspected=new LinkedHashSet(1);
        suspected.add(new Request(Request.SUSPECT,mbr,true));
        handleMembershipChange(suspected);
    }


    /**
     * Invoked upon receiving a MERGE event from the MERGE layer. Starts the merge protocol.
     * See description of protocol in DESIGN.
     * @param views A List of different views detected by the merge protocol
     */
    public void merge(Map views) {
        merger.merge(views);
    }

    public void handleMergeResponse(MergeData data, MergeId merge_id) {
        merger.handleMergeResponse(data, merge_id);
    }


    public void handleMergeCancelled(MergeId merge_id) {
        merger.handleMergeCancelled(merge_id);
    }


    /**
     * Fetches the digests from all members and installs them again. Used only for diagnosis and support; don't
     * use this otherwise !
     */
    void fixDigests() {
        merger.fixDigests();
    }



    public void handleMembershipChange(Collection requests) {
        boolean joinAndStateTransferInitiated=false;
        boolean useFlushIfPresent=gms.use_flush_if_present;
        Collection
new_mbrs=new LinkedHashSet
(requests.size()); Collection
suspected_mbrs=new LinkedHashSet
(requests.size()); Collection
leaving_mbrs=new LinkedHashSet
(requests.size()); boolean self_leaving=false; // is the coord leaving for(Request req: requests) { switch(req.type) { case Request.JOIN: new_mbrs.add(req.mbr); if(req.useFlushIfPresent) useFlushIfPresent=true; break; case Request.JOIN_WITH_STATE_TRANSFER: new_mbrs.add(req.mbr); joinAndStateTransferInitiated=true; if(req.useFlushIfPresent) useFlushIfPresent=true; break; case Request.LEAVE: if(req.suspected) suspected_mbrs.add(req.mbr); else { leaving_mbrs.add(req.mbr); if(gms.local_addr != null && gms.local_addr.equals(req.mbr)) self_leaving=true; } break; case Request.SUSPECT: suspected_mbrs.add(req.mbr); break; } } new_mbrs.remove(gms.local_addr); // remove myself - cannot join myself (already joined) if(gms.getViewId() == null) { // we're probably not the coord anymore (we just left ourselves), let someone else do it // (client will retry when it doesn't get a response) log.debug("gms.view_id is null, I'm not the coordinator anymore (leaving=%b); " + "the new coordinator will handle the leave request", self_leaving); return; } List
current_members=gms.members.getMembers(); leaving_mbrs.retainAll(current_members); // remove all elements of leaving_mbrs which are not current members if(suspected_mbrs.remove(gms.local_addr)) log.warn("I am the coord and I'm being suspected -- will probably leave shortly"); suspected_mbrs.retainAll(current_members); // remove all elements of suspected_mbrs which are not current members // for the members that have already joined, return the current digest and membership for(Iterator
it=new_mbrs.iterator(); it.hasNext();) { Address mbr=it.next(); if(gms.members.contains(mbr)) { // already joined: return current digest and membership log.trace("%s: %s already present; returning existing view %s", gms.local_addr, mbr, gms.view); Tuple tuple=gms.getViewAndDigest(); if(tuple != null) gms.sendJoinResponse(new JoinRsp(tuple.getVal1(), tuple.getVal2()), mbr); else log.warn("%s: did not find a digest matching view %s; dropping JOIN-RSP", gms.local_addr, gms.view); it.remove(); // remove it anyway, even if we didn't find a digest matching the view (joiner will retry) } } if(new_mbrs.isEmpty() && leaving_mbrs.isEmpty() && suspected_mbrs.isEmpty()) { log.trace("%s: found no members to add or remove, will not create new view", gms.local_addr); return; } View new_view=gms.getNextView(new_mbrs, leaving_mbrs, suspected_mbrs); if(new_view.size() == 0 && gms.local_addr != null && gms.local_addr.equals(new_view.getCreator())) { if(self_leaving) gms.initState(); // in case connect() is called again return; } log.trace("%s: joiners=%s, suspected=%s, leaving=%s, new view: %s", gms.local_addr, new_mbrs, suspected_mbrs, leaving_mbrs, new_view); JoinRsp join_rsp=null; boolean hasJoiningMembers=!new_mbrs.isEmpty(); try { boolean successfulFlush =!useFlushIfPresent || !gms.flushProtocolInStack || gms.startFlush(new_view); if(!successfulFlush && hasJoiningMembers) { // Don't send a join response if the flush fails (http://jira.jboss.org/jira/browse/JGRP-759) // The joiner should block until the previous FLUSH completed sendLeaveResponses(leaving_mbrs); // we still have to send potential leave responses // but let the joining client timeout and send another join request return; } // we cannot garbage collect during joining a new member *if* we're the only member // Example: {A}, B joins, after returning JoinRsp to B, A garbage collects messages higher than those // in the digest returned to the client, so the client will *not* be able to ask for retransmission // of those messages if he misses them if(hasJoiningMembers) { gms.getDownProtocol().down(new Event(Event.SUSPEND_STABLE, MAX_SUSPEND_TIMEOUT)); // create a new digest, which contains the new members, minus left members MutableDigest join_digest=new MutableDigest(new_view.getMembersRaw()).set(gms.getDigest()); for(Address member: new_mbrs) join_digest.set(member,0,0); // ... and set the new members. their first seqno will be 1 // If the digest from NAKACK doesn't include all members of the view, we try once more; if it is still // incomplete, we don't send a JoinRsp back to the joiner(s). This shouldn't be a problem as they will retry if(join_digest.allSet() || join_digest.set(gms.getDigest()).allSet()) join_rsp=new JoinRsp(new_view, join_digest); else log.warn("%s: digest does not match view (missing seqnos for %s); dropping JOIN-RSP", gms.local_addr, Arrays.toString(join_digest.getNonSetMembers())); } sendLeaveResponses(leaving_mbrs); // no-op if no leaving members // we don't need to send the digest to existing members: https://issues.jboss.org/browse/JGRP-1317 gms.castViewChange(new_view, null, new_mbrs); gms.sendJoinResponses(join_rsp, new_mbrs); } finally { if(hasJoiningMembers) gms.getDownProtocol().down(new Event(Event.RESUME_STABLE)); if(!joinAndStateTransferInitiated && useFlushIfPresent) gms.stopFlush(); if(self_leaving) gms.initState(); // in case connect() is called again } } /** * Called by the GMS when a VIEW is received. * @param new_view The view to be installed * @param digest If view is a MergeView, digest contains the seqno digest of all members and has to * be set by GMS */ public void handleViewChange(View new_view, Digest digest) { if(leaving && !new_view.containsMember(gms.local_addr)) return; gms.installView(new_view, digest); } public void stop() { super.stop(); // sets leaving=false merger.stop(); } private void sendLeaveResponses(Collection
leaving_members) { for(Address address: leaving_members){ Message msg=new Message(address).setFlag(Message.Flag.OOB, Message.Flag.INTERNAL, Message.Flag.NO_RELIABILITY) .putHeader(gms.getId(), new GMS.GmsHeader(GMS.GmsHeader.LEAVE_RSP)); log.trace("%s: sending LEAVE response to %s", gms.local_addr, address); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy