package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.protocols.pbcast.NAKACK2; import org.jgroups.protocols.pbcast.STABLE; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Tests large retransmissions (https://issues.jboss.org/browse/JGRP-1868). Multicast equivalent to * {@link org.jgroups.protocols.UNICAST_RetransmitTest} * @author Bela Ban * @since 3.6 */ @Test(groups=Global.FUNCTIONAL,singleThreaded=true) public class NAKACK_RetransmitTest { protected JChannel a, b, c; protected static final int MAX_BUNDLE_SIZE=10000; protected static final int NUM_MSGS=50000; protected static final Method START_RETRANSMISSION, STOP_RETRANSMISSION; static { try { START_RETRANSMISSION=NAKACK2.class.getDeclaredMethod("startRetransmitTask"); START_RETRANSMISSION.setAccessible(true); STOP_RETRANSMISSION=NAKACK2.class.getDeclaredMethod("stopRetransmitTask"); STOP_RETRANSMISSION.setAccessible(true); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } } @BeforeMethod protected void setup() throws Exception { a=new JChannel(Util.getTestStack()).name("A"); b=new JChannel(Util.getTestStack()).name("B"); c=new JChannel(Util.getTestStack()).name("C"); change(a, b, c); a.connect("NAKACK_RetransmitTest"); b.connect("NAKACK_RetransmitTest"); c.connect("NAKACK_RetransmitTest"); Util.waitUntilAllChannelsHaveSameView(10000, 500, a, b, c); } @AfterMethod protected void destroy() {Util.close(c,b,a);} /** * Sends a number of messages, but discards every other message. The retransmission task in NAKACK2 is initially * disabled. Then starts the retransmission task, which should generate an XMIT-REQ which is larger than * TP.max_bundle_size, leading to endless retransmissions. With JGRP-1868 resolved, the receiver should get * all messages. * <p/> * https://issues.jboss.org/browse/JGRP-1868 */ public void testLargeRetransmission() throws Exception { a.setReceiver(new MyReceiver()); b.setReceiver(new MyReceiver()); c.setReceiver(new MyReceiver()); List<Integer> la=((NAKACK_RetransmitTest.MyReceiver)a.getReceiver()).getList(); List<Integer> lb=((NAKACK_RetransmitTest.MyReceiver)b.getReceiver()).getList(); List<Integer> lc=((NAKACK_RetransmitTest.MyReceiver)c.getReceiver()).getList(); stopRetransmission(a); insertDiscardProtocol(a); for(int i=1; i <= NUM_MSGS; i++) a.send(null, i); removeDiscardProtocol(a); startRetransmission(a); for(int i=0; i < 10; i++) { if(la.size() == NUM_MSGS && lb.size() == NUM_MSGS && lc.size() == NUM_MSGS) break; STABLE stable=a.getProtocolStack().findProtocol(STABLE.class); stable.gc(); Util.sleep(1000); } System.out.println("A.size(): " + la.size() + "\nB.size(): " + lb.size() + "\nC.size(): " + lc.size()); for(List<Integer> list: Arrays.asList(la,lb,lc)) { int expected=1; for(int num : list) { assert expected == num; assert num <= NUM_MSGS; expected++; } } } protected static void change(JChannel ... channels) { for(JChannel ch: channels) { TP transport=ch.getProtocolStack().getTransport(); transport.setMaxBundleSize(MAX_BUNDLE_SIZE); NAKACK2 nak=ch.getProtocolStack().findProtocol(NAKACK2.class); if(nak == null) throw new IllegalStateException("NAKACK2 not present in the stack"); nak.setValue("max_xmit_req_size", 5000); } } protected static class MyReceiver extends ReceiverAdapter { protected final List<Integer> list=new ArrayList<>(); public void receive(Message msg) { Integer num=msg.getObject(); list.add(num); } public List<Integer> getList() {return list;} } protected void stopRetransmission(JChannel ... channels) throws Exception { for(JChannel ch: channels) { NAKACK2 nak=ch.getProtocolStack().findProtocol(NAKACK2.class); STOP_RETRANSMISSION.invoke(nak); } } protected void startRetransmission(JChannel ... channels) throws Exception { for(JChannel ch: channels) { NAKACK2 nak=ch.getProtocolStack().findProtocol(NAKACK2.class); START_RETRANSMISSION.invoke(nak); } } protected static void insertDiscardProtocol(JChannel ... channels) { for(JChannel ch: channels) { ProtocolStack stack=ch.getProtocolStack(); stack.insertProtocolInStack(new DiscardEveryOtherMulticastMessage(), stack.getTransport(), ProtocolStack.Position.ABOVE); } } protected static void removeDiscardProtocol(JChannel ... channels) { for(JChannel ch: channels) ch.getProtocolStack().removeProtocol(DiscardEveryOtherMulticastMessage.class); } protected static void setLevel(String level, JChannel ... channels) { for(JChannel ch: channels) { Protocol prot=ch.getProtocolStack().findProtocol(NAKACK2.class); prot.level(level); } } protected static class DiscardEveryOtherMulticastMessage extends Protocol { protected boolean discard=false; public Object down(Message msg) { if(msg.dest() == null) { discard=!discard; if(discard) return null; } return down_prot.down(msg); } } }