package org.jgroups.tests; import org.jgroups.Global; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.blocks.*; import org.jgroups.protocols.SHARED_LOOPBACK; import org.jgroups.protocols.SHARED_LOOPBACK_PING; import org.jgroups.protocols.TP; import org.jgroups.protocols.UNICAST3; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.NAKACK2; import org.jgroups.stack.Protocol; 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.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; /** * @author Bela Ban * @since 3.3 */ @Test(groups=Global.TIME_SENSITIVE,singleThreaded=true) public class RpcDispatcherAsyncInvocationTest { protected JChannel a, b; protected RpcDispatcher disp1, disp2; protected final AtomicInteger count=new AtomicInteger(0); protected Method incr_method; @Test(enabled=false) public int incr() { Util.sleep(500); return count.incrementAndGet(); } @AfterMethod protected void destroy() {disp2.stop(); disp1.stop(); Util.close(b,a);} @BeforeMethod protected void init() throws Exception { incr_method=RpcDispatcherAsyncInvocationTest.class.getMethod("incr"); a=createChannel("A"); b=createChannel("B"); disp1=new RpcDispatcher(a, this); disp2=new RpcDispatcher(b, this); a.connect("RpcDispatcherAsyncInvocationTest"); b.connect("RpcDispatcherAsyncInvocationTest"); disp2.setRequestHandler(new MyRequestHandler()); disp2.asyncDispatching(true); count.set(0); } /** Tests that 10 sync regular RPCs (each taking 500ms) take roughly 5 seconds */ public void testRegularInvocation() throws Exception { invoke(10, false, 5000, 8000); } public void testRegularAsyncInvocation() throws Exception { invoke(10, false, 5000, 8000); } public void testOOBAsyncInvocation() throws Exception { invoke(10, true, 500, 1000); } protected void invoke(int num_invocations, boolean use_oob, long expected_min, long expected_max) throws Exception { long start=System.currentTimeMillis(); List<Integer> list=invokeRpc(num_invocations, use_oob); long time=System.currentTimeMillis() - start; System.out.println("took " + time + " ms: " + list); assert list.size() == num_invocations; assert time >= expected_min && time <= expected_max : // extreme GC could cause this (or even worse) "time was expected to be in range [" + expected_min + " .. " + expected_max + "] but was " + time; } protected List<Integer> invokeRpc(final int num_invocations, boolean use_oob) throws Exception { RequestOptions opts=RequestOptions.SYNC(); if(use_oob) opts.flags(Message.Flag.OOB); final List<Integer> results=new ArrayList<>(num_invocations); MethodCall call=new MethodCall(incr_method); for(int i=0; i < num_invocations; i++) { CompletableFuture<Object> future=disp1.callRemoteMethodWithFuture(b.getAddress(), call, opts); future.whenComplete((result,ex) -> { try { results.add((Integer)result); System.out.println("<-- " + result); if(results.size() == num_invocations) { synchronized(results) { results.notifyAll(); } } } catch(Exception ignored) {} }); } for(int i=0; i < 20; i++) { synchronized(results) { if(results.size() == num_invocations) break; results.wait(500); } } return results; } protected static JChannel createChannel(String name) throws Exception { TP transport=new SHARED_LOOPBACK(); transport.setThreadPoolMinThreads(10); transport.setThreadPoolMaxThreads(20); return new JChannel(new Protocol[]{ transport, new SHARED_LOOPBACK_PING(), new NAKACK2(), new UNICAST3(), new GMS() }).name(name); } protected class MyRequestHandler implements RequestHandler { public void handle(final Message request, final Response response) throws Exception { if(request.isFlagSet(Message.Flag.OOB)) { new Thread() { public void run() { int val=incr(); if(response != null) response.send(val, false); } }.start(); } else { int val=incr(); if(response != null) response.send(val, false); } } public Object handle(Message msg) throws Exception { return incr(); } } }