package org.jgroups.tests;
import org.jgroups.Global;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.jgroups.protocols.PING;
import org.jgroups.protocols.TUNNEL;
import org.jgroups.protocols.UNICAST3;
import org.jgroups.protocols.pbcast.GMS;
import org.jgroups.protocols.pbcast.NAKACK2;
import org.jgroups.protocols.pbcast.STABLE;
import org.jgroups.stack.GossipRouter;
import org.jgroups.util.Promise;
import org.jgroups.util.ResourceManager;
import org.jgroups.util.StackType;
import org.jgroups.util.Util;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.net.InetAddress;
/**
* Test designed to make sure the TUNNEL doesn't lock the client and the GossipRouter under load.
*
* @author Ovidiu Feodorov <ovidiu@feodorov.com>
* @see TUNNELDeadLockTest#testStress
*/
@Test(groups={Global.STACK_INDEPENDENT, Global.GOSSIP_ROUTER, Global.EAP_EXCLUDED},singleThreaded=true)
public class TUNNELDeadLockTest {
private JChannel channel;
private Promise<Boolean> promise;
private int receivedCnt;
// the total number of the messages pumped down the channel
private static final int msgCount=20_000;
// the time (in ms) the main thread waits for all the messages to arrive,
// before declaring the test failed.
private static final int mainTimeout=10000;
private String bind_addr="loopback";
GossipRouter gossipRouter;
private int gossip_router_port;
private String gossip_router_hosts;
@BeforeMethod
void setUp() throws Exception {
StackType type=Util.getIpStackType();
if(type == StackType.IPv6)
bind_addr="::1";
else
bind_addr="127.0.0.1";
promise=new Promise<>();
gossip_router_port=ResourceManager.getNextTcpPort(InetAddress.getByName(bind_addr));
gossip_router_hosts=bind_addr + "[" + gossip_router_port + "]";
gossipRouter=new GossipRouter(bind_addr, gossip_router_port).useNio(false);
gossipRouter.start();
}
@AfterMethod void tearDown() throws Exception {
//TUNNEL tunnel=channel.getProtocolStack().findProtocol(TUNNEL.class);
//System.out.printf("TUNNEL stats:\n%s\n", tunnel.getMessageStats());
Util.close(channel);
promise.reset();
promise=null;
gossipRouter.stop();
System.out.println("Router stopped");
}
/**
* Pushes messages down the channel as fast as possible. Sometimes this
* manages to bring the channel and the Router into deadlock. On the
* machine I run it usually happens after 700 - 1000 messages and I
* suspect that this number it is related to the socket buffer size.
* (the comments are written when I didn't solve the bug yet). <br>
* <p/>
* The number of messages sent can be controlled with msgCount.
* The time (in ms) the main threads wait for the all messages to come can
* be controlled with mainTimeout. If this time passes and the test
* doesn't see all the messages, it declares itself failed.
*/
public void testStress() throws Exception {
receivedCnt=0;
channel=createTunnelChannel("A");
channel.connect(TUNNELDeadLockTest.class.getSimpleName());
channel.setReceiver(new ReceiverAdapter() {
@Override
public void receive(Message msg) {
receivedCnt++;
if(receivedCnt % 2000 == 0)
System.out.println("-- received " + receivedCnt);
if(receivedCnt >= msgCount) {
// let the main thread know I got all msgs
promise.setResult(Boolean.TRUE);
}
}
});
// stress send messages - the sender thread
new Thread(() -> {
try {
for(int i=1; i <= msgCount; i++) {
channel.send(null, i);
if(i % 2000 == 0)
System.out.println("-- sent " + i);
}
}
catch(Exception e) {
System.err.println("Error sending data over ...");
e.printStackTrace();
}
}).start();
Boolean result=promise.getResult(mainTimeout);
if(result == null)
assert false: String.format("failed to receive %d messages in %d ms (%d messages received so far)",
msgCount, mainTimeout, receivedCnt);
}
protected JChannel createTunnelChannel(String name) throws Exception {
TUNNEL tunnel=(TUNNEL)new TUNNEL().setValue("bind_addr", InetAddress.getByName(bind_addr));
tunnel.setGossipRouterHosts(gossip_router_hosts);
JChannel ch=new JChannel(tunnel,
new PING(),
new NAKACK2(),
new UNICAST3(),
new STABLE(),
new GMS().joinTimeout(1000)).name(name);
if(name != null)
ch.setName(name);
return ch;
}
}