package com.lambdaworks.redis.cluster;
import static com.google.code.tempusfugit.temporal.Duration.seconds;
import static com.google.code.tempusfugit.temporal.Timeout.timeout;
import static com.lambdaworks.redis.cluster.ClusterTestUtil.getOwnPartition;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.*;
import org.junit.runners.MethodSorters;
import com.google.code.tempusfugit.temporal.Duration;
import com.google.code.tempusfugit.temporal.ThreadSleep;
import com.google.code.tempusfugit.temporal.WaitFor;
import com.lambdaworks.Wait;
import com.lambdaworks.category.SlowTests;
import com.lambdaworks.redis.*;
import com.lambdaworks.redis.api.StatefulRedisConnection;
import com.lambdaworks.redis.cluster.api.sync.RedisClusterCommands;
import com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@SuppressWarnings("unchecked")
@SlowTests
public class RedisClusterStressScenariosTest extends AbstractTest {
public static final String host = TestSettings.hostAddr();
protected static RedisClient client;
protected static RedisClusterClient clusterClient;
protected Logger log = LogManager.getLogger(getClass());
protected StatefulRedisConnection<String, String> redis5;
protected StatefulRedisConnection<String, String> redis6;
protected RedisClusterCommands<String, String> redissync5;
protected RedisClusterCommands<String, String> redissync6;
protected String key = "key";
protected String value = "value";
@Rule
public ClusterRule clusterRule = new ClusterRule(clusterClient, AbstractClusterTest.port5, AbstractClusterTest.port6);
@BeforeClass
public static void setupClient() throws Exception {
client = RedisClient.create(RedisURI.Builder.redis(host, AbstractClusterTest.port5).build());
clusterClient = RedisClusterClient.create(
Collections.singletonList(RedisURI.Builder.redis(host, AbstractClusterTest.port5)
.build()));
}
@AfterClass
public static void shutdownClient() {
FastShutdown.shutdown(client);
}
@Before
public void before() throws Exception {
ClusterSetup.setupMasterWithSlave(clusterRule);
redis5 = client.connect(RedisURI.Builder.redis(host, AbstractClusterTest.port5).build());
redis6 = client.connect(RedisURI.Builder.redis(host, AbstractClusterTest.port6).build());
redissync5 = redis5.sync();
redissync6 = redis6.sync();
clusterClient.reloadPartitions();
WaitFor.waitOrTimeout(() -> {
return clusterRule.isStable();
}, timeout(seconds(5)), new ThreadSleep(Duration.millis(500)));
}
@After
public void after() throws Exception {
redis5.close();
redissync5.close();
redissync6.close();
}
@Test
public void testClusterFailover() throws Exception {
log.info("Cluster node 5 is master");
log.info("Cluster nodes seen from node 5:\n" + redissync5.clusterNodes());
log.info("Cluster nodes seen from node 6:\n" + redissync6.clusterNodes());
Wait.untilTrue(() -> getOwnPartition(redissync5).is(RedisClusterNode.NodeFlag.MASTER)).waitOrTimeout();
Wait.untilTrue(() -> getOwnPartition(redissync6).is(RedisClusterNode.NodeFlag.SLAVE)).waitOrTimeout();
String failover = redissync6.clusterFailover(true);
assertThat(failover).isEqualTo("OK");
Wait.untilTrue(() -> getOwnPartition(redissync6).is(RedisClusterNode.NodeFlag.MASTER)).waitOrTimeout();
Wait.untilTrue(() -> getOwnPartition(redissync5).is(RedisClusterNode.NodeFlag.SLAVE)).waitOrTimeout();
log.info("Cluster nodes seen from node 5 after clusterFailover:\n" + redissync5.clusterNodes());
log.info("Cluster nodes seen from node 6 after clusterFailover:\n" + redissync6.clusterNodes());
RedisClusterNode redis5Node = getOwnPartition(redissync5);
RedisClusterNode redis6Node = getOwnPartition(redissync6);
assertThat(redis5Node.getFlags()).contains(RedisClusterNode.NodeFlag.SLAVE);
assertThat(redis6Node.getFlags()).contains(RedisClusterNode.NodeFlag.MASTER);
}
@Test
public void testClusterConnectionStability() throws Exception {
RedisAdvancedClusterAsyncCommandsImpl<String, String> connection = (RedisAdvancedClusterAsyncCommandsImpl<String, String>) clusterClient
.connectClusterAsync();
RedisChannelHandler<String, String> statefulConnection = (RedisChannelHandler) connection.getStatefulConnection();
connection.set("a", "b");
ClusterDistributionChannelWriter<String, String> writer = (ClusterDistributionChannelWriter) statefulConnection
.getChannelWriter();
StatefulRedisConnectionImpl<Object, Object> statefulSlotConnection = (StatefulRedisConnectionImpl) writer
.getClusterConnectionProvider().getConnection(ClusterConnectionProvider.Intent.WRITE, 3300);
final RedisAsyncConnection<Object, Object> slotConnection = statefulSlotConnection.async();
slotConnection.set("a", "b");
slotConnection.close();
WaitFor.waitOrTimeout(() -> !slotConnection.isOpen(), timeout(seconds(5)));
assertThat(statefulSlotConnection.isClosed()).isTrue();
assertThat(statefulSlotConnection.isOpen()).isFalse();
assertThat(connection.isOpen()).isTrue();
assertThat(statefulConnection.isOpen()).isTrue();
assertThat(statefulConnection.isClosed()).isFalse();
try {
connection.set("a", "b");
} catch (RedisException e) {
assertThat(e).hasMessageContaining("Connection is closed");
}
connection.close();
}
@Test(timeout = 20000)
public void distributedClusteredAccessAsync() throws Exception {
RedisClusterAsyncConnection<String, String> connection = clusterClient.connectClusterAsync();
List<RedisFuture<?>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
futures.add(connection.set("a" + i, "myValue1" + i));
futures.add(connection.set("b" + i, "myValue2" + i));
futures.add(connection.set("d" + i, "myValue3" + i));
}
for (RedisFuture<?> future : futures) {
future.get();
}
for (int i = 0; i < 100; i++) {
RedisFuture<String> setA = connection.get("a" + i);
RedisFuture<String> setB = connection.get("b" + i);
RedisFuture<String> setD = connection.get("d" + i);
setA.get();
setB.get();
setD.get();
assertThat(setA.getError()).isNull();
assertThat(setB.getError()).isNull();
assertThat(setD.getError()).isNull();
assertThat(setA.get()).isEqualTo("myValue1" + i);
assertThat(setB.get()).isEqualTo("myValue2" + i);
assertThat(setD.get()).isEqualTo("myValue3" + i);
}
connection.close();
}
@Test
public void distributedClusteredAccessSync() throws Exception {
RedisClusterConnection<String, String> connection = clusterClient.connectCluster();
for (int i = 0; i < 100; i++) {
connection.set("a" + i, "myValue1" + i);
connection.set("b" + i, "myValue2" + i);
connection.set("d" + i, "myValue3" + i);
}
for (int i = 0; i < 100; i++) {
assertThat(connection.get("a" + i)).isEqualTo("myValue1" + i);
assertThat(connection.get("b" + i)).isEqualTo("myValue2" + i);
assertThat(connection.get("d" + i)).isEqualTo("myValue3" + i);
}
connection.close();
}
}