package org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.*; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.pbcast.NAKACK2; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.Protocol; import org.jgroups.util.Digest; import org.jgroups.util.MutableDigest; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.*; /** * Tests merging with SEQUENCER * @author Bela Ban * @since 3.1 */ @Test(groups=Global.STACK_INDEPENDENT,sequential=true) public class SequencerMergeTest { JChannel a, b, c, d; static final String GROUP="SequencerMergeTest"; @BeforeMethod void setUp() throws Exception { } @AfterMethod void tearDown() throws Exception { Util.close(d, c, b, a); } /** * Tests a merge between {A} and {B,C,D}, plus a concurrent multicast. * https://issues.jboss.org/browse/JGRP-1468 */ public void testMergeAndSendOrdering() throws Exception { // Create subgroup {A}: a=create("A", true); a.connect(GROUP); // Create subgroup {B,C,D} b=create("B", false); b.connect(GROUP); c=create("C", false); d=create("D", false); Util.sleep(1000); c.connect(GROUP); d.connect(GROUP); Util.waitUntilAllChannelsHaveSameSize(10000, 1000, b,c,d); removeDiscard(a,b,c,d); System.out.println("Channels:\n" + printChannels(a,b,c,d)); MyReceiver ra=new MyReceiver("A"); MyReceiver rb=new MyReceiver("B"); MyReceiver rc=new MyReceiver("C"); MyReceiver rd=new MyReceiver("D"); a.setReceiver(ra); b.setReceiver(rb); c.setReceiver(rc); d.setReceiver(rd); final View new_view=Util.createView(a.getAddress(), 5, a.getAddress(),b.getAddress(),c.getAddress(),d.getAddress()); final Digest digest=getDigest(a,b,c,d); System.out.println("Installing " + new_view + " in B,C and D"); injectViewAndDigest(new_view,digest,b,c,d); System.out.println("Channels:\n" + printChannels(a,b,c,d)); assert Util.isCoordinator(a); assert !Util.isCoordinator(b); assert !Util.isCoordinator(c); assert !Util.isCoordinator(d); Thread thread=new Thread() { public void run() { Util.sleep(1000); // Finally installing the new view at A; this simulates a delayed view installation System.out.println("Installing " + new_view + " in A"); injectViewAndDigest(new_view, getDigest(), a); } }; thread.start(); System.out.println("D sends a multicast message M"); Message msg=new Message(null, "M"); d.send(msg); System.out.println("\nReceivers:"); List<String> list_a=ra.getList(); List<String> list_b=rb.getList(); List<String> list_c=rc.getList(); List<String> list_d=rd.getList(); final List<String> expected=Arrays.asList("V5", "M"); for(int i=0; i < 20; i++) { boolean all_ok=true; for(List<String> list: new ArrayList<List<String>>(Arrays.asList(list_a, list_b, list_c, list_d))) { if(!list.equals(expected)) { all_ok=false; break; } } if(all_ok) break; Util.sleep(500); } System.out.println("A: " + list_a + "\nB: " + list_b + "\nC: " + list_c + "\nD: " + list_d); for(List<String> list: Arrays.asList(list_a, list_b, list_c, list_d)) assert list.equals(expected) : "expected=" + expected + ", actual list=" + list; System.out.println("OK: order of all 3 lists is correct"); } /** * Tests a merge between {D,A} and {B,C,D}. * https://issues.jboss.org/browse/JGRP-1484 */ public void testMergeWithParticipant() throws Exception { a=create("A", false); b=create("B", false); c=create("C", false); d=create("D", false); a.connect(GROUP); b.connect(GROUP); c.connect(GROUP); d.connect(GROUP); Util.waitUntilAllChannelsHaveSameSize(10000, 1000, a,b,c,d); Util.sleep(1000); System.out.println("Channels:\n" + printChannels(a,b,c,d)); MyReceiver ra=new MyReceiver("A"); MyReceiver rb=new MyReceiver("B"); MyReceiver rc=new MyReceiver("C"); MyReceiver rd=new MyReceiver("D"); a.setReceiver(ra); b.setReceiver(rb); c.setReceiver(rc); d.setReceiver(rd); // To have the best chance of hitting JGRP-1484, construct A's view so that his coordinator isn't coordinator after merging. JChannel a_coord; if (c.getAddress().compareTo(d.getAddress()) < 0) a_coord = d; else a_coord = c; // Inject either {C,A} or {D,A} at A final View a_view=Util.createView(a_coord.getAddress(), 10, a_coord.getAddress(),a.getAddress()); final Digest a_digest=getDigest(a_coord,a); System.out.println("\nInstalling " + a_view + " in A"); injectViewAndDigest(a_view, a_digest, a); assert !Util.isCoordinator(a); // Inject {B,C,D} at B,C,D final View bcd_view=Util.createView(b.getAddress(), 20, b.getAddress(),c.getAddress(),d.getAddress()); final Digest bcd_digest=getDigest(b,c,d); System.out.println("\nInstalling " + bcd_view + " in B,C and D"); injectViewAndDigest(bcd_view,bcd_digest,b,c,d); assert Util.isCoordinator(b); assert !Util.isCoordinator(c); assert !Util.isCoordinator(d); // start merging final Map<Address,View> views=new HashMap<Address,View>(); views.put(a.getAddress(), a.getView()); views.put(b.getAddress(), b.getView()); views.put(c.getAddress(), c.getView()); views.put(d.getAddress(), d.getView()); final Event merge_evt=new Event(Event.MERGE, views); System.out.println("\n==== Injecting a merge event (leader=" + b.getAddress() + ") ===="); injectMergeEvent(merge_evt, b); // Wait for merge to complete. Util.waitUntilAllChannelsHaveSameSize(20000, 1000, a,b,c,d); final View merged_view = a.getView(); System.out.println("\nMerged view is " + merged_view); } protected JChannel create(String name, boolean insert_discard) throws Exception { JChannel ch=new JChannel(new SHARED_LOOPBACK(), new DISCARD().setValue("discard_all", insert_discard), new PING().setValue("timeout",100), new NAKACK2().setValue("use_mcast_xmit",false) .setValue("log_discard_msgs",false).setValue("log_not_found_msgs",false), new UNICAST2(), new STABLE().setValue("max_bytes",50000), new SEQUENCER(), // below GMS, to establish total order between views and messages new GMS().setValue("print_local_addr",false).setValue("leave_timeout",100) .setValue("log_view_warnings",false).setValue("view_ack_collection_timeout",50) .setValue("log_collect_msgs",false)); ch.setName(name); return ch; } protected static void injectViewAndDigest(View view, Digest digest, JChannel ... channels) { for(JChannel ch: channels) { GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class); gms.installView(view); Protocol nak=ch.getProtocolStack().findProtocol(NAKACK.class, NAKACK2.class); if(nak != null) nak.down(new Event(Event.SET_DIGEST, digest)); } } private static void injectMergeEvent(Event evt, JChannel ... channels) { for(JChannel ch: channels) { GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class); gms.up(evt); } } protected static Digest getDigest(JChannel ... channels) { MutableDigest digest=new MutableDigest(channels.length); for(JChannel ch: channels) { Protocol nak=ch.getProtocolStack().findProtocol(NAKACK.class, NAKACK2.class); Digest tmp=(Digest)nak.down(new Event(Event.GET_DIGEST, ch.getAddress())); if(tmp != null) digest.add(tmp); } return digest; } protected static void makeCoordinator(JChannel ch) { GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class); gms.becomeCoordinator(); } protected static String printChannels(JChannel ... channels) { StringBuilder sb=new StringBuilder(); for(JChannel ch: channels) { sb.append(ch.getName() + ": view is " + ch.getView() + "\n"); } return sb.toString(); } protected static void removeDiscard(JChannel ... channels) { for(JChannel ch: channels) ch.getProtocolStack().removeProtocol(DISCARD.class); } protected static class MyReceiver extends ReceiverAdapter { protected final String name; protected final List<String> list=new ArrayList<String>(); public MyReceiver(String name) { this.name=name; } public List<String> getList() { return list; } public void receive(Message msg) { list.add(msg.getObject().toString()); } public void viewAccepted(View view) { String tmp="V" + view.getViewId().getId(); list.add(tmp); } } }