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.getNodeId; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.junit.*; import org.junit.runners.MethodSorters; import com.google.code.tempusfugit.temporal.WaitFor; import com.lambdaworks.redis.*; import com.lambdaworks.redis.api.StatefulRedisConnection; import com.lambdaworks.redis.cluster.api.async.RedisClusterAsyncCommands; import com.lambdaworks.redis.cluster.api.sync.RedisAdvancedClusterCommands; import com.lambdaworks.redis.cluster.api.sync.RedisClusterCommands; import com.lambdaworks.redis.cluster.models.slots.ClusterSlotRange; import com.lambdaworks.redis.cluster.models.slots.ClusterSlotsParser; import com.lambdaworks.redis.internal.LettuceLists; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ClusterCommandTest extends AbstractClusterTest { protected static RedisClient client; protected StatefulRedisConnection<String, String> connection; protected RedisClusterAsyncCommands<String, String> async; protected RedisClusterCommands<String, String> sync; @BeforeClass public static void setupClient() throws Exception { client = RedisClient.create(RedisURI.Builder.redis(host, port1).build()); clusterClient = RedisClusterClient.create(LettuceLists.newList(RedisURI.Builder.redis(host, port1).build())); } @AfterClass public static void shutdownClient() { FastShutdown.shutdown(client); FastShutdown.shutdown(clusterClient); } @Before public void before() throws Exception { clusterRule.getClusterClient().reloadPartitions(); connection = client.connect(RedisURI.Builder.redis(host, port1).build()); sync = connection.sync(); async = connection.async(); } @After public void after() throws Exception { connection.close(); } @Test public void statefulConnectionFromSync() throws Exception { RedisAdvancedClusterConnection<String, String> sync = clusterClient.connectCluster(); assertThat(sync.getStatefulConnection().sync()).isSameAs(sync); } @Test public void statefulConnectionFromAsync() throws Exception { RedisAsyncConnection<String, String> async = client.connectAsync(); assertThat(async.getStatefulConnection().async()).isSameAs(async); } @Test public void testClusterBumpEpoch() throws Exception { RedisFuture<String> future = async.clusterBumpepoch(); String result = future.get(); assertThat(result).matches("(BUMPED|STILL).*"); } @Test public void testClusterInfo() throws Exception { RedisFuture<String> future = async.clusterInfo(); String result = future.get(); assertThat(result).contains("cluster_known_nodes:"); assertThat(result).contains("cluster_slots_fail:0"); assertThat(result).contains("cluster_state:"); } @Test public void testClusterNodes() throws Exception { String result = sync.clusterNodes(); assertThat(result).contains("connected"); assertThat(result).contains("master"); assertThat(result).contains("myself"); } @Test public void testClusterNodesSync() throws Exception { RedisClusterConnection<String, String> connection = clusterClient.connectCluster(); String string = connection.clusterNodes(); connection.close(); assertThat(string).contains("connected"); assertThat(string).contains("master"); assertThat(string).contains("myself"); } @Test public void testClusterSlaves() throws Exception { sync.set("b", value); RedisFuture<Long> replication = async.waitForReplication(1, 5); assertThat(replication.get()).isGreaterThan(0L); } @Test public void testAsking() throws Exception { assertThat(sync.asking()).isEqualTo("OK"); } @Test public void testReset() throws Exception { clusterClient.reloadPartitions(); RedisAdvancedClusterAsyncCommandsImpl<String, String> connection = (RedisAdvancedClusterAsyncCommandsImpl) clusterClient .connectClusterAsync(); RedisFuture<String> setA = connection.set("a", "myValue1"); setA.get(); connection.reset(); setA = connection.set("a", "myValue1"); assertThat(setA.getError()).isNull(); assertThat(setA.get()).isEqualTo("OK"); connection.close(); } @Test public void testClusterSlots() throws Exception { List<Object> reply = sync.clusterSlots(); assertThat(reply.size()).isGreaterThan(1); List<ClusterSlotRange> parse = ClusterSlotsParser.parse(reply); assertThat(parse).hasSize(2); ClusterSlotRange clusterSlotRange = parse.get(0); assertThat(clusterSlotRange.getFrom()).isEqualTo(0); assertThat(clusterSlotRange.getTo()).isEqualTo(11999); assertThat(clusterSlotRange.getMaster()).isNotNull(); assertThat(clusterSlotRange.getSlaves()).isNotNull(); assertThat(clusterSlotRange.toString()).contains(ClusterSlotRange.class.getSimpleName()); } @Test public void readOnly() throws Exception { // cluster node 3 is a slave for key "b" String key = "b"; assertThat(SlotHash.getSlot(key)).isEqualTo(3300); prepareReadonlyTest(key); // assume cluster node 3 is a slave for the master 1 RedisConnection<String, String> connect3 = client.connect(RedisURI.Builder.redis(host, port3).build()).sync(); assertThat(connect3.readOnly()).isEqualTo("OK"); waitUntilValueIsVisible(key, connect3); String resultBViewedBySlave = connect3.get("b"); assertThat(resultBViewedBySlave).isEqualTo(value); connect3.quit(); resultBViewedBySlave = connect3.get("b"); assertThat(resultBViewedBySlave).isEqualTo(value); } @Test public void readOnlyWithReconnect() throws Exception { // cluster node 3 is a slave for key "b" String key = "b"; assertThat(SlotHash.getSlot(key)).isEqualTo(3300); prepareReadonlyTest(key); // assume cluster node 3 is a slave for the master 1 RedisConnection<String, String> connect3 = client.connect(RedisURI.Builder.redis(host, port3).build()).sync(); assertThat(connect3.readOnly()).isEqualTo("OK"); connect3.quit(); waitUntilValueIsVisible(key, connect3); String resultViewedBySlave = connect3.get("b"); assertThat(resultViewedBySlave).isEqualTo(value); } protected void waitUntilValueIsVisible(String key, RedisConnection<String, String> connection) throws InterruptedException, TimeoutException { WaitFor.waitOrTimeout(() -> connection.get(key) != null, timeout(seconds(5))); } protected void prepareReadonlyTest(String key) throws InterruptedException, TimeoutException, java.util.concurrent.ExecutionException { async.set(key, value); String resultB = async.get(key).get(); assertThat(resultB).isEqualTo(value); Thread.sleep(500); // give some time to replicate } @Test public void readOnlyReadWrite() throws Exception { // cluster node 3 is a slave for key "b" String key = "b"; assertThat(SlotHash.getSlot(key)).isEqualTo(3300); prepareReadonlyTest(key); // assume cluster node 3 is a slave for the master 1 final RedisConnection<String, String> connect3 = client.connect(RedisURI.Builder.redis(host, port3).build()).sync(); try { connect3.get("b"); } catch (Exception e) { assertThat(e).hasMessageContaining("MOVED"); } assertThat(connect3.readOnly()).isEqualTo("OK"); waitUntilValueIsVisible(key, connect3); connect3.readWrite(); try { connect3.get("b"); } catch (Exception e) { assertThat(e).hasMessageContaining("MOVED"); } } @Test public void clusterSlaves() throws Exception { String nodeId = getNodeId(sync); List<String> result = sync.clusterSlaves(nodeId); assertThat(result.size()).isGreaterThan(0); } }