package org.infinispan.client.hotrod.stress; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.Callable; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; import org.infinispan.client.hotrod.RemoteCache; import org.infinispan.client.hotrod.RemoteCacheManager; import org.infinispan.client.hotrod.annotation.ClientCacheEntryCreated; import org.infinispan.client.hotrod.annotation.ClientCacheEntryModified; import org.infinispan.client.hotrod.annotation.ClientListener; import org.infinispan.client.hotrod.configuration.ConfigurationBuilder; import org.infinispan.client.hotrod.event.ClientEvent; import org.infinispan.client.hotrod.logging.Log; import org.infinispan.client.hotrod.logging.LogFactory; import org.infinispan.client.hotrod.test.HotRodClientTestingUtil; import org.infinispan.client.hotrod.test.InternalRemoteCacheManager; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.server.hotrod.HotRodServer; import org.infinispan.test.SingleCacheManagerTest; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.testng.annotations.Test; @Test(groups = "stress", testName = "client.hotrod.event.ClientEventStressTest", timeOut = 15*60*1000) public class ClientEventStressTest extends SingleCacheManagerTest { private static final Log log = LogFactory.getLog(ClientEventStressTest.class); static int NUM_CLIENTS = 3; static int NUM_THREADS_PER_CLIENT = 10; static final int NUM_OPERATIONS = 10_000; static final int NUM_EVENTS = NUM_OPERATIONS * NUM_THREADS_PER_CLIENT * NUM_CLIENTS * NUM_CLIENTS; static ExecutorService EXEC = Executors.newCachedThreadPool(); HotRodServer hotrodServer; @Override protected EmbeddedCacheManager createCacheManager() throws Exception { return TestCacheManagerFactory.createCacheManager(); } @Override protected void setup() throws Exception { super.setup(); hotrodServer = HotRodClientTestingUtil.startHotRodServer(cacheManager); } RemoteCacheManager getRemoteCacheManager(int port) { ConfigurationBuilder builder = new ConfigurationBuilder(); builder.addServer().host("127.0.0.1").port(port); return new InternalRemoteCacheManager(builder.build()); } public void testStressEvents() { CyclicBarrier barrier = new CyclicBarrier((NUM_CLIENTS * NUM_THREADS_PER_CLIENT) + 1); List<Future<Void>> futures = new ArrayList<>(NUM_CLIENTS * NUM_THREADS_PER_CLIENT); List<ClientEntryListener> listeners = new ArrayList<>(NUM_CLIENTS); RemoteCacheManager[] remotecms = new RemoteCacheManager[NUM_CLIENTS]; for (int i = 0; i < NUM_CLIENTS; i++) remotecms[i] = getRemoteCacheManager(hotrodServer.getPort()); for (RemoteCacheManager remotecm : remotecms) { RemoteCache<Integer, Integer> remote = remotecm.getCache(); ClientEntryListener listener = new ClientEntryListener(); listeners.add(listener); remote.addClientListener(listener); for (int i = 0; i < NUM_THREADS_PER_CLIENT; i++) { Callable<Void> call = new Put(barrier, remote); futures.add(EXEC.submit(call)); } } barrierAwait(barrier); // wait for all threads to be ready barrierAwait(barrier); // wait for all threads to finish for (Future<Void> f : futures) futureGet(f); log.debugf("Put operations completed, wait for events..."); eventuallyEquals(NUM_EVENTS, () -> countEvents(listeners)); } int countEvents(List<ClientEntryListener> listeners) { Integer count = listeners.stream().reduce(0, (acc, l) -> acc + l.count.get(), (x, y) -> x + y); log.debugf("Event count is %d, target %d%n", (int) count, NUM_EVENTS); return count; } static class Put implements Callable<Void> { static final ThreadLocalRandom R = ThreadLocalRandom.current(); final CyclicBarrier barrier; final RemoteCache<Integer, Integer> remote; public Put(CyclicBarrier barrier, RemoteCache<Integer, Integer> remote) { this.barrier = barrier; this.remote = remote; } @Override public Void call() throws Exception { barrierAwait(barrier); try { for (int i = 0; i < NUM_OPERATIONS; i++) { int value = R.nextInt(Integer.MAX_VALUE); remote.put(value, value); // TODO: Consider using async puts } return null; } finally { barrierAwait(barrier); } } } @ClientListener static class ClientEntryListener { final AtomicInteger count = new AtomicInteger(); @ClientCacheEntryCreated @ClientCacheEntryModified @SuppressWarnings("unused") public void handleClientEvent(ClientEvent event) { int countSoFar; if ((countSoFar = count.incrementAndGet()) % 100 == 0) { log.debugf("Reached %s", countSoFar); } } } static int barrierAwait(CyclicBarrier barrier) { try { return barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { throw new AssertionError(e); } } <T> T futureGet(Future<T> future) { try { return future.get(); } catch (InterruptedException | ExecutionException e) { throw new AssertionError(e); } } }