package org.rakam.kume.service.ringmap; import com.google.common.collect.ImmutableList; import org.rakam.kume.Cluster; import org.rakam.kume.ClusterBuilder; import org.rakam.kume.ClusterMembership; import org.rakam.kume.JoinerService; import org.rakam.kume.KumeTest; import org.rakam.kume.Member; import org.rakam.kume.MigrationListener; import org.rakam.kume.NoNetworkTransport; import org.rakam.kume.service.ServiceListBuilder; import org.rakam.kume.service.crdt.counter.GCounterService; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import static java.util.stream.IntStream.range; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class RingMapTest extends KumeTest { // @Test public void testMapNotEnoughNodeForReplication() throws InterruptedException, TimeoutException, ExecutionException, IOException { ImmutableList<ServiceListBuilder.Constructor> services = new ServiceListBuilder() .add("map", bus -> new RingMap<String, Long>(bus, GCounterService::merge, 2)).build(); Cluster cluster0 = new ClusterBuilder().services(services).start(); RingMap ringMap0 = cluster0.getService("map"); for (int i = 0; i < 1000; i++) { ringMap0.put("test" + System.currentTimeMillis() + i, i).get(); } assertEquals(ringMap0.getLocalSize(), 1000); } // @Test public void testMapReplication() throws InterruptedException, TimeoutException, ExecutionException { ImmutableList<ServiceListBuilder.Constructor> services = new ServiceListBuilder() .add("map", bus -> new RingMap<String, Long>(bus, GCounterService::merge, 2)).build(); List<Cluster> clusters = createFixedFakeCluster(2, services).map(ClusterBuilder::start).collect(Collectors.toList());; RingMap ringMap0 = clusters.get(0).getService("map"); RingMap ringMap1 = clusters.get(1).getService("map"); for (int i = 0; i < 100000; i++) { ringMap0.put("test" + i, 5).get(); } assertEquals(ringMap1.getLocalSize(), ringMap1.getLocalSize()); for (int i = 0; i < ringMap0.getBucketCount(); i++) { assertEquals(ringMap0.getBucket(i), ringMap1.getBucket(i)); } } // @Test public void testMapDistribution() throws InterruptedException, TimeoutException, ExecutionException { ImmutableList<ServiceListBuilder.Constructor> services = new ServiceListBuilder() .add("map", bus -> new RingMap<String, Long>(bus, GCounterService::merge, 1)).build(); List<Cluster> clusters = createFixedFakeCluster(2, services).map(ClusterBuilder::start).collect(Collectors.toList()); RingMap ringMap0 = clusters.get(0).getService("map"); RingMap ringMap1 = clusters.get(1).getService("map"); for (int i = 0; i < 100000; i++) { ringMap0.put("test" + i, 5).get(); } assertEquals(ringMap1.getLocalSize()+ringMap0.getLocalSize(), 100000); } // @Test public void testMapNewNode() throws InterruptedException, TimeoutException, ExecutionException { ImmutableList<ServiceListBuilder.Constructor> services = new ServiceListBuilder() .add("map", bus -> new RingMap<String, Long>(bus, GCounterService::merge, 2)).build(); Member baseMember = new Member("", 0); NoNetworkTransport baseTransport = new NoNetworkTransport(baseMember); ProxyJoinerService joinerService = new ProxyJoinerService(); Cluster cluster0 = new ClusterBuilder().transport(baseTransport::setContext) .joinStrategy(joinerService).services(services).serverAddress(baseMember.getAddress()) .start(); RingMap ringMap0 = cluster0.getService("map"); for (int i = 0; i < 100000; i++) { ringMap0.put("test" + i, 5).get(); } List<Cluster> newNodes = createFixedFakeCluster(range(1, 3), services).map(e -> { e.members().add(baseMember); return e.start(); }).collect(Collectors.toList()); RingMap ringMap1 = cluster0.getService("map"); BlockerMigrationListener blocker = new BlockerMigrationListener(); ringMap1.listenMigrations(blocker); newNodes.stream().map(Cluster::getTransport).forEach(tr -> baseTransport.addMember((NoNetworkTransport) tr)); newNodes.stream().map(Cluster::getLocalMember).forEach(joinerService::addMember); blocker.waitForMigrationEnd(); Thread.sleep(2000); System.out.println(1); // CompletableFuture<Map<Member, Integer>> size1 = ringMap1.size(); // Integer size = size1.join().values().stream().reduce((x, y) -> x + y).get(); // assertEquals(size.intValue(), 2000); } // @Test public void testMapNodeFailure() throws InterruptedException, TimeoutException, ExecutionException { ImmutableList<ServiceListBuilder.Constructor> services = new ServiceListBuilder() .add("map", bus -> new RingMap<String, Long>(bus, GCounterService::merge, 2)).build(); Cluster cluster0 = new ClusterBuilder().services(services).start(); Cluster cluster1 = new ClusterBuilder().services(services).start(); Cluster cluster2 = new ClusterBuilder().services(services).start(); waitForDiscovery(cluster0, 3); waitForDiscovery(cluster1, 3); waitForDiscovery(cluster2, 3); RingMap ringMap0 = cluster0.getService("map"); for (int i = 0; i < 1000; i++) { ringMap0.put("test" + i, 5).get(); } cluster2.close(); waitForMigrationEnd(ringMap0); CompletableFuture<Map<Member, Integer>> size1 = ringMap0.size(); Optional<Integer> test = size1.get().values().stream().reduce((i0, i1) -> i0 + i1); assertTrue(test.isPresent()); assertEquals(test.get().intValue(), 200); } private void waitForMigrationEnd(RingMap ringMap0) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(1); ringMap0.listenMigrations(new BlockerMigrationListener()); countDownLatch.await(); } // @Test public void testMapMultipleThreads() throws InterruptedException, TimeoutException, ExecutionException { ImmutableList<ServiceListBuilder.Constructor> services = new ServiceListBuilder() .add("map", bus -> new RingMap<String, Long>(bus, GCounterService::merge, 2)).build(); Cluster cluster0 = new ClusterBuilder().services(services).start(); new ClusterBuilder().services(services).start(); waitForDiscovery(cluster0, 1); RingMap ringMap0 = cluster0.getService("map"); RingMap ringMap1 = cluster0.getService("map"); CountDownLatch countDownLatch = new CountDownLatch(2); for (int i = 0; i < 10; i++) { final int finalI = i; new Thread(() -> { try { for (int i1 = 0; i1 < 100; i1++) { ringMap0.put("s0" + finalI + i1, finalI + i1).get(); } } catch (Exception e) { e.printStackTrace(); } countDownLatch.countDown(); }).run(); new Thread(() -> { try { for (int i1 = 0; i1 < 100; i1++) { ringMap1.put("s1" + finalI + i1, finalI + i1).get(); } } catch (Exception e) { e.printStackTrace(); } countDownLatch.countDown(); }).run(); } countDownLatch.await(); CompletableFuture<Map<Member, Integer>> size = ringMap1.size(); Optional<Integer> test = size.get().values().stream().reduce((i0, i1) -> i0 + i1); assertTrue(test.isPresent()); assertEquals(test.get().intValue(), 2000 * 2); } private static class ProxyJoinerService implements JoinerService { public ClusterMembership clusterMembership; @Override public void onStart(ClusterMembership membership) { this.clusterMembership = membership; } public void addMember(Member member) { clusterMembership.addMember(member); } } private static class BlockerMigrationListener implements MigrationListener { private CountDownLatch countDownLatch; public BlockerMigrationListener() { this.countDownLatch = new CountDownLatch(1); } @Override public void migrationStart(Member removedMember) { } @Override public void migrationEnd(Member removedMember) { countDownLatch.countDown(); } public void waitForMigrationEnd() throws InterruptedException { countDownLatch.await(); synchronized (this) { countDownLatch = new CountDownLatch(1); } } } }