org.jgroups.protocols.pbcast.CoordGmsImpl Maven / Gradle / Ivy
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