public static void testDetermineMergeParticipantsAndMergeCoords4() { Address a = Util.createRandomAddress(), b = Util.createRandomAddress(), c = Util.createRandomAddress(), d = Util.createRandomAddress(); org.jgroups.util.UUID.add(a, "A"); org.jgroups.util.UUID.add(b, "B"); org.jgroups.util.UUID.add(c, "C"); org.jgroups.util.UUID.add(d, "D"); View v1 = View.create(a, 1, a, b); View v2 = View.create(c, 1, c, d); Map<Address, View> map = new HashMap<>(); map.put(a, v1); map.put(b, v1); map.put(d, v2); StringBuilder sb = new StringBuilder("map:\n"); for (Map.Entry<Address, View> entry : map.entrySet()) sb.append(entry.getKey() + ": " + entry.getValue() + "\n"); System.out.println(sb); Collection<Address> merge_participants = Util.determineMergeParticipants(map); System.out.println("merge_participants = " + merge_participants); assert merge_participants.size() == 3; assert merge_participants.contains(a) && merge_participants.contains(c) && merge_participants.contains(d); Collection<Address> merge_coords = Util.determineMergeCoords(map); System.out.println("merge_coords = " + merge_coords); assert merge_coords.size() == 2; assert merge_coords.contains(a) && merge_coords.contains(c); }
public static void testGetBits() { SeqnoRange range = new SeqnoRange(1, 100); System.out.println("range = " + range); assert range.size() == 100; Collection<Range> bits = range.getBits(false); assert bits.size() == 1; Range tmp = bits.iterator().next(); assert tmp.low == 1 && tmp.high == 100; range.set(1, 2); assert range.size() == 100; bits = range.getBits(true); assert bits != null && bits.size() == 1; // 1 range: [1-2] tmp = bits.iterator().next(); assert tmp.low == 1 && tmp.high == 2; for (int i = 1; i < 100; i++) range.set(i); bits = range.getBits(false); assert bits.size() == 1; tmp = bits.iterator().next(); assert tmp.low == 100 && tmp.high == 100; for (int i = 1; i <= range.size(); i++) range.clear(i); for (int i = 2; i <= 99; i++) range.set(i); bits = range.getBits(true); assert bits.size() == 1; tmp = bits.iterator().next(); assert tmp.low == 2 && tmp.high == 99; bits = range.getBits(false); assert bits.size() == 2; tmp = bits.iterator().next(); assert tmp.low == 1 && tmp.high == 1; Iterator<Range> it = bits.iterator(); it.next(); tmp = it.next(); assert tmp.low == 100 && tmp.high == 100; }
/** * 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 <em>different</em> views detected by the merge protocol */ public void merge(Map<Address, View> views) { if (isMergeInProgress()) { if (log.isTraceEnabled()) log.trace(gms.local_addr + ": merge is already running (merge_id=" + merge_id + ")"); return; } // we need the merge *coordinators* not merge participants because not everyone can lead a merge // ! Collection<Address> coords = Util.determineActualMergeCoords(views); if (coords.isEmpty()) { log.error( gms.local_addr + ": unable to determine merge leader from " + views + "; not starting a merge"); return; } Membership tmp = new Membership(coords); // establish a deterministic order, so that coords can elect leader tmp.sort(); Address merge_leader = tmp.elementAt(0); if (merge_leader.equals(gms.local_addr)) { if (log.isDebugEnabled()) { Collection<Address> merge_participants = Util.determineMergeParticipants(views); log.debug( "I (" + gms.local_addr + ") will be the leader. Starting the merge task for " + merge_participants.size() + " coords"); } merge_task.start(views); } else { if (log.isTraceEnabled()) log.trace( "I (" + gms.local_addr + ") am not the merge leader, " + "waiting for merge leader (" + merge_leader + ") to initiate merge"); } }
public void handleMembershipChange(Collection<Request> requests) { boolean joinAndStateTransferInitiated = false; boolean useFlushIfPresent = gms.use_flush_if_present; Collection<Address> new_mbrs = new LinkedHashSet<>(requests.size()); Collection<Address> suspected_mbrs = new LinkedHashSet<>(requests.size()); Collection<Address> 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<Address> 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<Address> 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<View, Digest> 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 } }
public static void testSet() { SeqnoRange range = new SeqnoRange(10, 15); range.set(11, 12, 13, 14); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfReceivedMessages() == 4; assert range.getNumberOfMissingMessages() == 2; Collection<Range> xmits = range.getMessagesToRetransmit(); assert xmits.size() == 2; Iterator<Range> it = xmits.iterator(); Range r = it.next(); assert r.low == 10 && r.high == 10; r = it.next(); assert r.low == 15 && r.high == 15; range = new SeqnoRange(10, 15); range.set(10, 11, 12, 13, 14); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfReceivedMessages() == 5; assert range.getNumberOfMissingMessages() == 1; xmits = range.getMessagesToRetransmit(); assert xmits.size() == 1; it = xmits.iterator(); r = it.next(); assert r.low == 15 && r.high == 15; range = new SeqnoRange(10, 15); range.set(11, 12, 13, 14, 15); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfReceivedMessages() == 5; assert range.getNumberOfMissingMessages() == 1; xmits = range.getMessagesToRetransmit(); assert xmits.size() == 1; it = xmits.iterator(); r = it.next(); assert r.low == 10 && r.high == 10; range = new SeqnoRange(10, 15); range.set(10, 11, 12, 13, 14, 15); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfReceivedMessages() == 6; assert range.getNumberOfMissingMessages() == 0; xmits = range.getMessagesToRetransmit(); assert xmits.isEmpty(); range = new SeqnoRange(10, 15); range.set(11, 12, 14, 15); System.out.println("range=" + print(range)); assert range.size() == 6; assert range.getNumberOfReceivedMessages() == 4; assert range.getNumberOfMissingMessages() == 2; xmits = range.getMessagesToRetransmit(); assert xmits.size() == 2; it = xmits.iterator(); r = it.next(); assert r.low == 10 && r.high == 10; r = it.next(); assert r.low == 13 && r.high == 13; range.set(13); assert range.getNumberOfReceivedMessages() == 5; assert range.getNumberOfMissingMessages() == 1; xmits = range.getMessagesToRetransmit(); it = xmits.iterator(); r = it.next(); assert r.low == 10 && r.high == 10; range.set(10); System.out.println("range=" + print(range)); assert range.getNumberOfReceivedMessages() == 6; assert range.getNumberOfMissingMessages() == 0; xmits = range.getMessagesToRetransmit(); assert xmits.isEmpty(); }