diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index d72accd63d..60a0096644 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -3189,6 +3189,11 @@ public RedisFuture zunionstore(K destination, ZStoreArgs zStoreArgs, K... return dispatch(commandBuilder.zunionstore(destination, zStoreArgs, keys)); } + @Override + public RedisFuture>> clusterLinks() { + return dispatch(commandBuilder.clusterLinks()); + } + private byte[] encodeFunction(String functionCode) { LettuceAssert.notNull(functionCode, "Function code must not be null"); LettuceAssert.notEmpty(functionCode, "Function code script must not be empty"); diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index 675bc31ba3..8614a6eb59 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -3257,6 +3257,11 @@ public Mono zunionstore(K destination, ZStoreArgs zStoreArgs, K... keys) { return createMono(() -> commandBuilder.zunionstore(destination, zStoreArgs, keys)); } + @Override + public Mono>> clusterLinks() { + return createMono(commandBuilder::clusterLinks); + } + private byte[] encodeFunction(String functionCode) { LettuceAssert.notNull(functionCode, "Function code must not be null"); LettuceAssert.notEmpty(functionCode, "Function code script must not be empty"); diff --git a/src/main/java/io/lettuce/core/RedisCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCommandBuilder.java index 46e5cb809b..a192690d9e 100644 --- a/src/main/java/io/lettuce/core/RedisCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCommandBuilder.java @@ -4433,6 +4433,11 @@ Command zunionstore(K destination, ZAggregateArgs aggregateArgs, K.. return createCommand(ZUNIONSTORE, new IntegerOutput<>(codec), args); } + Command>> clusterLinks() { + CommandArgs args = new CommandArgs<>(codec).add(LINKS); + return createCommand(CLUSTER, (CommandOutput) new ObjectOutput<>(StringCodec.UTF8), args); + } + private boolean allElementsInstanceOf(Object[] objects, Class expectedAssignableType) { for (Object object : objects) { diff --git a/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java index 2835968fd0..6f9e1e9020 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java @@ -380,4 +380,11 @@ public interface RedisClusterAsyncCommands extends BaseRedisAsyncCommands< */ RedisFuture readWrite(); + /** + * Retrieves information about the TCP links between nodes in a Redis Cluster. + * + * @return List of maps containing attributes and values for each peer link. + */ + RedisFuture>> clusterLinks(); + } diff --git a/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java b/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java index 5f1d611f90..2416b380ec 100644 --- a/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java @@ -21,6 +21,7 @@ import java.time.Duration; import java.util.List; +import java.util.Map; import io.lettuce.core.Range; import io.lettuce.core.api.reactive.*; @@ -368,4 +369,11 @@ public interface RedisClusterReactiveCommands extends BaseRedisReactiveCom */ Mono readWrite(); + /** + * Retrieves information about the TCP links between nodes in a Redis Cluster. + * + * @return List of maps containing attributes and values for each peer link. + */ + Mono>> clusterLinks(); + } diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java index 2e18bc68da..ec02e83a9b 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java @@ -21,6 +21,7 @@ import java.time.Duration; import java.util.List; +import java.util.Map; import io.lettuce.core.Range; import io.lettuce.core.api.sync.*; @@ -368,4 +369,11 @@ public interface RedisClusterCommands extends BaseRedisCommands, Red @Override String readWrite(); + /** + * Retrieves information about the TCP links between nodes in a Redis Cluster. + * + * @return List of maps containing attributes and values for each peer link. + */ + List> clusterLinks(); + } diff --git a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java index a422ad1f1d..bd56b713dc 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java +++ b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java @@ -39,7 +39,7 @@ public enum CommandKeyword implements ProtocolKeyword { FAILOVER, FORGET, FIELDS, FLAGS, FLUSH, FORCE, FREQ, FLUSHSLOTS, GENPASS, GETNAME, GETUSER, GETKEYSINSLOT, GETREDIR, GROUP, GROUPS, HTSTATS, ID, IDLE, INFO, - IDLETIME, JUSTID, KILL, KEYSLOT, LEFT, LEN, LIMIT, LIST, LOAD, LOG, MATCH, + IDLETIME, JUSTID, KILL, KEYSLOT, LEFT, LEN, LIMIT, LINKS, LIST, LOAD, LOG, MATCH, MAX, MAXLEN, MEET, MIN, MINID, MOVED, NO, NOACK, NOCOMMANDS, NODE, NODES, NOMKSTREAM, NOPASS, NOSAVE, NOT, NOVALUES, NUMSUB, SHARDCHANNELS, SHARDNUMSUB, NUMPAT, NX, OFF, ON, ONE, OR, PAUSE, PREFIXES, diff --git a/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java index f99393cc18..16276b1703 100644 --- a/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisCommandBuilderUnitTests.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; @@ -168,4 +170,14 @@ void shouldCorrectlyConstructClusterMyshardid() { .isEqualTo("*2\r\n" + "$7\r\n" + "CLUSTER\r\n" + "$9\r\n" + "MYSHARDID\r\n"); } + @Test + void shouldCorrectlyConstructClusterLinks() { + + Command>> command = sut.clusterLinks(); + ByteBuf buf = Unpooled.directBuffer(); + command.encode(buf); + + assertThat(buf.toString(StandardCharsets.UTF_8)).isEqualTo("*2\r\n$7\r\nCLUSTER\r\n$5\r\nLINKS\r\n"); + } + } diff --git a/src/test/java/io/lettuce/core/cluster/ClusterCommandIntegrationTests.java b/src/test/java/io/lettuce/core/cluster/ClusterCommandIntegrationTests.java index 2cdff16990..ae92d0f231 100644 --- a/src/test/java/io/lettuce/core/cluster/ClusterCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cluster/ClusterCommandIntegrationTests.java @@ -5,6 +5,7 @@ import java.time.Duration; import java.util.List; +import java.util.Map; import javax.inject.Inject; @@ -271,6 +272,26 @@ void clusterReplicas() { assertThat(result.size()).isGreaterThan(0); } + @Test + void testClusterLinks() { + List> values = sync.clusterLinks(); + assertThat(values).isNotEmpty(); + for (Map value : values) { + assertThat(value).containsKeys("direction", "node", "create-time", "events", "send-buffer-allocated", + "send-buffer-used"); + } + } + + @Test + void testClusterLinksAsync() throws Exception { + RedisFuture>> futureLinks = async.clusterLinks(); + List> values = futureLinks.get(); + for (Map value : values) { + assertThat(value).containsKeys("direction", "node", "create-time", "events", "send-buffer-allocated", + "send-buffer-used"); + } + } + private void prepareReadonlyTest(String key) { async.set(key, value); diff --git a/src/test/java/io/lettuce/core/cluster/ClusterReactiveCommandIntegrationTests.java b/src/test/java/io/lettuce/core/cluster/ClusterReactiveCommandIntegrationTests.java index 74ddc18d0d..ec9a315829 100644 --- a/src/test/java/io/lettuce/core/cluster/ClusterReactiveCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cluster/ClusterReactiveCommandIntegrationTests.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.List; +import java.util.Map; import javax.inject.Inject; @@ -97,4 +98,13 @@ void clusterSlaves() { assertThat(result.size()).isGreaterThan(0); } + @Test + void testClusterLinks() { + List> values = reactive.clusterLinks().block(); + for (Map value : values) { + assertThat(value).containsKeys("direction", "node", "create-time", "events", "send-buffer-allocated", + "send-buffer-used"); + } + } + }