package org.jgroups.tests; import org.jgroups.*; import org.jgroups.protocols.*; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK2; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; /** * Tests the reliable FIFO (NAKACK{2}) protocol * <p/> * Two sender peers send 1000 messages to the group, where each message contains * a long value, mirroring seqnos used. A receiver peer receives the messages * from each sender and checks that seqnos are received in the correct order. * <p/> * An object all_msgs_recd is used to allow the main test thread to discover when * all sent messages have been received. * <p/> * The test case passes if the expected number of messages is received, and messages * are received in order from each sender. This implies that: * (i) all messages from each peer were received (reliable) and * (ii) all messages from each peer are received in order (FIFO) * @author Richard Achmatowicz * @author Bela Ban */ @Test(groups=Global.FUNCTIONAL, singleThreaded=true) public class NakackTest { final static int NUM_PEERS=3; final static int NUM_SENDERS=NUM_PEERS-1; final static int NUM_MSGS=1000; final static int MSGS_PER_STATUS_LINE=500; final static int TOT_MSGS_FOR_ALL_RECEIVERS=NUM_SENDERS * NUM_MSGS * NUM_PEERS; // convey assertion failure from thread to main framework static boolean notFIFO=false; JChannel[] channels=new JChannel[NUM_PEERS]; Thread[] threads=new Thread[NUM_PEERS]; //define senders and receivers boolean[] isSender={false,true,true}; protected final AtomicInteger received_msgs=new AtomicInteger(0); @BeforeMethod protected void setUp() throws Exception { for(int i=0; i < NUM_PEERS; i++) { channels[i]=createChannel().name(Character.toString((char)(i + 'A'))); channels[i].connect("NakackTest"); } org.jgroups.Receiver[] receivers=new org.jgroups.Receiver[NUM_PEERS]; // set up the sender and the receiver callbacks, according to whether the peer is a sender or a receiver for(int i=0; i < NUM_PEERS; i++) { receivers[i]=new Receiver(channels[i]); channels[i].setReceiver(receivers[i]); } Util.waitUntilAllChannelsHaveSameView(10000, 1000, channels); } @AfterMethod void tearDown() throws Exception { Util.close(channels); } /** * Test to see thyat NAKACK delivery is reliable and FIFO. */ public void testReceptionOfAllMessages() throws TimeoutException { // start the NAKACK peers and let them exchange messages for(int i=0; i < NUM_PEERS; i++) { threads[i]=new Sender(channels[i], isSender[i]); threads[i].start(); } // wait for the threads to terminate try { for(int i=0; i < NUM_PEERS; i++) threads[i].join(); } catch(InterruptedException e) { } // wait for the receiver peer to signal that it has received messages, or timeout for(int i=0; i < 20; i++) { if(received_msgs.get() >= TOT_MSGS_FOR_ALL_RECEIVERS) break; Util.sleep(500); } // the test fails if: // - a seqno is received out of order (not FIFO), or // - not all messages are received in time allotted (allMsgsReceived) Assert.assertTrue(received_msgs.get() == TOT_MSGS_FOR_ALL_RECEIVERS, "Incorrect number of messages received by the receiver thread"); Assert.assertFalse(notFIFO, "Sequenece numbers for a peer not in correct order"); } protected static JChannel createChannel() throws Exception { Protocol[] protocols={ new SHARED_LOOPBACK(), new SHARED_LOOPBACK_PING(), new MERGE3().setValue("min_interval", 1000).setValue("max_interval", 3000), new NAKACK2().setValue("use_mcast_xmit", false), new UNICAST3(), new STABLE().setValue("max_bytes", 50000), new GMS().setValue("print_local_addr", false), new UFC(), new MFC(), new FRAG2() }; return new JChannel(protocols); } /** * This method should do the following: * - receive messages from senders * - check that sequence numbers for each sender are in order (with no gaps) * - terminate when correct number of messages have been received */ protected class Receiver extends ReceiverAdapter { final JChannel channel; ConcurrentMap<Address, Long> senders=new ConcurrentHashMap<>(); public Receiver(JChannel channel) { this.channel=channel; } /** * Receive() is concurrent for different senders, but sequential per sender * @param msg */ public void receive(Message msg) { // keep track of seqno ordering of messages received Address sender=msg.getSrc(); received_msgs.incrementAndGet(); // get the expected next seqno for this sender Long num=senders.get(sender); if(num == null) { num=(long)1; senders.putIfAbsent(sender, num); } long last_seqno=num; try { num=(Long)msg.getObject(); long received_seqno=num; // 1. check if sequence numbers are in sequence if(received_seqno == last_seqno) // correct - update with next expected seqno senders.put(sender,last_seqno + 1); else { // error, terminate test notFIFO=true; Assert.fail("FAIL: received msg #" + received_seqno + ", expected " + last_seqno); } Address address=channel.getAddress(); if(received_seqno % MSGS_PER_STATUS_LINE == 0 && received_seqno > 0) System.out.println("<" + address + ">:" + "PASS: received msg #" + received_seqno + " from " + sender); } catch(Exception ex) { System.err.println(ex.toString()); } } } static class Sender extends Thread { JChannel ch=null; boolean sender=false; public Sender(JChannel ch, boolean sender) { this.ch=ch; this.sender=sender; } public void run() { // senders send NUM_MSGS messages to all peers, beginning with seqno 1 if(sender) { Address address=ch.getAddress(); for(int i=1; i <= NUM_MSGS; i++) { try { Message msg=new Message(null, (long)i).src(address); ch.send(msg); if(i % MSGS_PER_STATUS_LINE == 0) // status indicator System.out.println("<" + address + ">:" + " ==> " + i); } catch(Exception e) { e.printStackTrace(); } } } } } }