package io.scalecube.cluster;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import io.scalecube.cluster.membership.MembershipEvent;
import io.scalecube.testlib.BaseTest;
import io.scalecube.transport.Message;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Anton Kharenko
*/
@RunWith(Parameterized.class)
public class ClusterMessagingStressTest extends BaseTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ClusterMessagingStressTest.class);
private static List<Object[]> experiments = Arrays.asList(new Object[][] {
// Msg count
{ 1_000 }, // warm up
{ 1_000 },
{ 5_000 },
{ 10_000 },
{ 25_000 },
// { 50_000 },
// { 100_000 },
// { 250_000 },
// { 500_000 },
// { 1_000_000 },
});
// Maximum time to await for all responses
private static final int timeoutSeconds = 60;
@Parameterized.Parameters(name = "msgCount={0}")
public static List<Object[]> data() {
return experiments;
}
private final int msgCount;
public ClusterMessagingStressTest(int msgCount) {
this.msgCount = msgCount;
}
@Test
public void clusterMessagingStressTest() throws Exception {
// Init transports
Cluster echoServer = Cluster.joinAwait();
Cluster[] latentNodes = new Cluster[ClusterConfig.DEFAULT_PING_REQ_MEMBERS];
Cluster client1 = null;
// Init measured params
long sentTime = 0;
long receivedTime = 0;
LongSummaryStatistics rttStats = null;
// Run experiment
try {
// Subscribe echo server handler
echoServer.listen().subscribe(msg -> echoServer.send(msg.sender(), msg));
// Start latent nodes (for indirect pings)
for (int i = 0; i < latentNodes.length; i++) {
latentNodes[i] = Cluster.joinAwait(echoServer.address());
}
// Init client
CountDownLatch measureLatch = new CountDownLatch(msgCount);
ArrayList<Long> rttRecords = new ArrayList<>(msgCount);
client1 = Cluster.joinAwait(echoServer.address());
client1.listen().subscribe(msg -> {
long sentAt = Long.valueOf(msg.data());
long rttTime = System.currentTimeMillis() - sentAt;
rttRecords.add(rttTime);
measureLatch.countDown();
});
// Subscribe on member removed event
AtomicBoolean receivedMemberRemovedEvent = new AtomicBoolean(false);
client1.listenMembership()
.filter(MembershipEvent::isRemoved)
.subscribe(event -> {
LOGGER.warn("Received member removed event: {}", event);
receivedMemberRemovedEvent.set(true);
});
// Measure
long startAt = System.currentTimeMillis();
for (int i = 0; i < msgCount; i++) {
client1.send(echoServer.address(), Message.fromData(Long.toString(System.currentTimeMillis())));
}
sentTime = System.currentTimeMillis() - startAt;
measureLatch.await(timeoutSeconds, TimeUnit.SECONDS);
receivedTime = System.currentTimeMillis() - startAt;
rttStats = rttRecords.stream().mapToLong(v -> v).summaryStatistics();
assertTrue(measureLatch.getCount() == 0);
assertFalse("Received member removed event", receivedMemberRemovedEvent.get());
} finally {
// Print results
LOGGER.info("Finished sending {} messages in {} ms", msgCount, sentTime);
LOGGER.info("Finished receiving {} messages in {} ms", msgCount, receivedTime);
LOGGER.info("Round trip stats (ms): {}", rttStats);
// Shutdown
shutdown(echoServer);
shutdown(latentNodes);
shutdown(client1);
}
}
private void shutdown(Cluster... nodes) {
for (Cluster node : nodes) {
try {
node.shutdown().get();
} catch (Exception ex) {
LOGGER.error("Exception on cluster shutdown", ex);
}
}
}
}