package org.jgroups.tests.byteman;
import org.jboss.byteman.contrib.bmunit.BMNGRunner;
import org.jboss.byteman.contrib.bmunit.BMScript;
import org.jgroups.*;
import org.jgroups.protocols.FORWARD_TO_COORD;
import org.jgroups.protocols.PING;
import org.jgroups.protocols.SHARED_LOOPBACK;
import org.jgroups.protocols.UNICAST2;
import org.jgroups.protocols.pbcast.GMS;
import org.jgroups.protocols.pbcast.NAKACK2;
import org.jgroups.util.Util;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/**
* Tests FIFO ordering of {@link FORWARD_TO_COORD} with message sending while the coordinator crashes
* @author Bela Ban
*/
@Test(groups=Global.BYTEMAN,sequential=true)
public class ForwardToCoordFailoverTest extends BMNGRunner {
JChannel a, b, c; // A is the coordinator
static final String CLUSTER="ForwardToCoordFailoverTest";
@BeforeMethod
void setUp() throws Exception {
a=createChannel("A", CLUSTER);
b=createChannel("B", CLUSTER);
c=createChannel("C", CLUSTER);
Util.waitUntilAllChannelsHaveSameSize(10000, 500, a,b,c);
}
@AfterMethod
void tearDown() throws Exception {
Util.close(c, b, a);
}
/**
* Tests that resending of messages in the forward-queue on a view change and sending of new messages at the
* same time doesn't lead to incorrect ordering (forward-queue messages need to be delivered before new msgs).
* <p/>
* C sends messages while A leaves, we need to make sure that the messages sent by C are received in FIFO order by B.
* <p/>
* https://issues.jboss.org/browse/JGRP-1517
*/
@BMScript(dir="scripts/ForwardToCoordFailoverTest", value="testSendingDuringViewChange")
public void testSendingDuringViewChange() throws Exception {
MyReceiver rb=new MyReceiver("B");
b.setReceiver(rb);
final int EXPECTED_MSGS=10;
// Now kill A (the coordinator)
System.out.print("-- killing A: ");
Util.shutdown(a);
System.out.println("done");
a=null;
// Now send message 1-5 (they'll end up in the forward-queue of FORWARD_TO_COORD)
System.out.println("-- sending message 1-5");
for(int i=1; i <= 5; i++) {
Event evt=new Event(Event.FORWARD_TO_COORD, new Message(null, i));
c.down(evt);
}
// now inject view B={B,C} into B and C
View view=Util.createView(b.getAddress(), 5, b.getAddress(), c.getAddress());
System.out.println("Injecting view " + view + " into B and C");
for(JChannel ch: Arrays.asList(c,b)) {
GMS gms=(GMS)ch.getProtocolStack().findProtocol(GMS.class);
gms.installView(view);
}
// Now wait for the view change, the sending of new messages 2-5 and the resending of 1, and make sure
// 1 is delivered before 2-5
List<Integer> list_b=rb.getList();
for(int i=0; i < 10; i++) {
if(list_b.size() == EXPECTED_MSGS)
break;
Util.sleep(1000);
}
System.out.println("\nB: " + list_b);
assert list_b.size() == EXPECTED_MSGS : "expected " + EXPECTED_MSGS + " msgs, but got " + list_b.size() + ": " +list_b;
System.out.println("OK: B has the expected number of messages (" + EXPECTED_MSGS + ")");
int seqno=1;
for(int i=0; i < EXPECTED_MSGS; i++) {
Integer seqno_b=list_b.get(i);
assert seqno_b == seqno : "expected " + seqno + " , but got " + seqno_b + " (B)";
seqno++;
}
System.out.println("OK: B's messages are in the correct order");
}
protected static class MyReceiver extends ReceiverAdapter {
protected final List<Integer> list=new LinkedList<Integer>();
protected final String name;
public MyReceiver(String name) {
this.name=name;
}
public List<Integer> getList() {return list;}
public int size() {return list.size();}
public void receive(Message msg) {
synchronized(list) {
list.add((Integer)msg.getObject());
}
}
}
protected static class MySender extends Thread {
protected final int rank;
protected final JChannel ch;
public MySender(int rank, JChannel ch) {
this.rank=rank;
this.ch=ch;
setName("sender-" + rank);
}
public void run() {
for(int i=1; i <=2; i++) {
Message msg=new Message(null, (rank + i));
try {
System.out.println("[" + rank + "]: sending msg " + (rank + i));
ch.send(msg);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
}
protected JChannel createChannel(final String name, final String cluster_name) throws Exception {
JChannel retval=new JChannel(new SHARED_LOOPBACK(),
// new DISCARD(),
new PING().setValue("timeout", 500),
new NAKACK2(),
new UNICAST2(),
new GMS(),
new FORWARD_TO_COORD());
retval.setName(name);
retval.connect(cluster_name);
return retval;
}
}