Skip to content

Commit 452ea52

Browse files
wormogigKhokhlov Alekseysazzad16
authored
Add CLUSTER SHARDS command support (#3598)
* Add CLUSTER SHARDS command (#2984) * Added new classes for CLUSTER SHARDS response * Slots type changed to List<List<Long>> + style fixed * Apply suggestions from code review * reset redis before ClusterCommandTest added * isSsl() added to ClusterShardNodeInfo * Several assertion added to clusterShards test --------- Co-authored-by: Khokhlov Aleksey <[email protected]> Co-authored-by: M Sazzadul Hoque <[email protected]>
1 parent 701df24 commit 452ea52

File tree

7 files changed

+314
-5
lines changed

7 files changed

+314
-5
lines changed

src/main/java/redis/clients/jedis/BuilderFactory.java

+102
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,108 @@ public Map<String, CommandInfo> build(Object data) {
997997
}
998998
};
999999

1000+
private static final Builder<List<List<Long>>> CLUSTER_SHARD_SLOTS_RANGES = new Builder<List<List<Long>>>() {
1001+
1002+
@Override
1003+
public List<List<Long>> build(Object data) {
1004+
if (null == data) {
1005+
return null;
1006+
}
1007+
1008+
List<Long> rawSlots = (List<Long>) data;
1009+
List<List<Long>> slotsRanges = new ArrayList<>();
1010+
for (int i = 0; i < rawSlots.size(); i += 2) {
1011+
slotsRanges.add(Arrays.asList(rawSlots.get(i), rawSlots.get(i + 1)));
1012+
}
1013+
return slotsRanges;
1014+
}
1015+
};
1016+
1017+
private static final Builder<List<ClusterShardNodeInfo>> CLUSTER_SHARD_NODE_INFO_LIST
1018+
= new Builder<List<ClusterShardNodeInfo>>() {
1019+
1020+
final Map<String, Builder> mappingFunctions = createDecoderMap();
1021+
1022+
private Map<String, Builder> createDecoderMap() {
1023+
1024+
Map<String, Builder> tempMappingFunctions = new HashMap<>();
1025+
tempMappingFunctions.put(ClusterShardNodeInfo.ID, STRING);
1026+
tempMappingFunctions.put(ClusterShardNodeInfo.ENDPOINT, STRING);
1027+
tempMappingFunctions.put(ClusterShardNodeInfo.IP, STRING);
1028+
tempMappingFunctions.put(ClusterShardNodeInfo.HOSTNAME, STRING);
1029+
tempMappingFunctions.put(ClusterShardNodeInfo.PORT, LONG);
1030+
tempMappingFunctions.put(ClusterShardNodeInfo.TLS_PORT, LONG);
1031+
tempMappingFunctions.put(ClusterShardNodeInfo.ROLE, STRING);
1032+
tempMappingFunctions.put(ClusterShardNodeInfo.REPLICATION_OFFSET, LONG);
1033+
tempMappingFunctions.put(ClusterShardNodeInfo.HEALTH, STRING);
1034+
1035+
return tempMappingFunctions;
1036+
}
1037+
1038+
@Override
1039+
@SuppressWarnings("unchecked")
1040+
public List<ClusterShardNodeInfo> build(Object data) {
1041+
if (null == data) {
1042+
return null;
1043+
}
1044+
1045+
List<ClusterShardNodeInfo> response = new ArrayList<>();
1046+
1047+
List<Object> clusterShardNodeInfos = (List<Object>) data;
1048+
for (Object clusterShardNodeInfoObject : clusterShardNodeInfos) {
1049+
List<Object> clusterShardNodeInfo = (List<Object>) clusterShardNodeInfoObject;
1050+
Iterator<Object> iterator = clusterShardNodeInfo.iterator();
1051+
response.add(new ClusterShardNodeInfo(createMapFromDecodingFunctions(iterator, mappingFunctions)));
1052+
}
1053+
1054+
return response;
1055+
}
1056+
1057+
@Override
1058+
public String toString() {
1059+
return "List<ClusterShardNodeInfo>";
1060+
}
1061+
};
1062+
1063+
public static final Builder<List<ClusterShardInfo>> CLUSTER_SHARD_INFO_LIST
1064+
= new Builder<List<ClusterShardInfo>>() {
1065+
1066+
final Map<String, Builder> mappingFunctions = createDecoderMap();
1067+
1068+
private Map<String, Builder> createDecoderMap() {
1069+
1070+
Map<String, Builder> tempMappingFunctions = new HashMap<>();
1071+
tempMappingFunctions.put(ClusterShardInfo.SLOTS, CLUSTER_SHARD_SLOTS_RANGES);
1072+
tempMappingFunctions.put(ClusterShardInfo.NODES, CLUSTER_SHARD_NODE_INFO_LIST);
1073+
1074+
return tempMappingFunctions;
1075+
}
1076+
1077+
@Override
1078+
@SuppressWarnings("unchecked")
1079+
public List<ClusterShardInfo> build(Object data) {
1080+
if (null == data) {
1081+
return null;
1082+
}
1083+
1084+
List<ClusterShardInfo> response = new ArrayList<>();
1085+
1086+
List<Object> clusterShardInfos = (List<Object>) data;
1087+
for (Object clusterShardInfoObject : clusterShardInfos) {
1088+
List<Object> clusterShardInfo = (List<Object>) clusterShardInfoObject;
1089+
Iterator<Object> iterator = clusterShardInfo.iterator();
1090+
response.add(new ClusterShardInfo(createMapFromDecodingFunctions(iterator, mappingFunctions)));
1091+
}
1092+
1093+
return response;
1094+
}
1095+
1096+
@Override
1097+
public String toString() {
1098+
return "List<ClusterShardInfo>";
1099+
}
1100+
};
1101+
10001102
public static final Builder<List<Module>> MODULE_LIST = new Builder<List<Module>>() {
10011103
@Override
10021104
public List<Module> build(Object data) {

src/main/java/redis/clients/jedis/Jedis.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -6386,7 +6386,7 @@ public String srandmember(final String key) {
63866386
* @param key
63876387
* @param count if positive, return an array of distinct elements.
63886388
* If negative the behavior changes and the command is allowed to
6389-
* return the same element multiple times
6389+
* return the same element multiple times
63906390
* @return A list of randomly selected elements
63916391
*/
63926392
@Override
@@ -8758,7 +8758,7 @@ public long clusterKeySlot(final String key) {
87588758
public long clusterCountFailureReports(final String nodeId) {
87598759
checkIsInMultiOrPipeline();
87608760
connection.sendCommand(CLUSTER, "COUNT-FAILURE-REPORTS", nodeId);
8761-
return connection.getIntegerReply();
8761+
return connection.getIntegerReply();
87628762
}
87638763

87648764
@Override
@@ -8826,12 +8826,20 @@ public String clusterFailover(ClusterFailoverOption failoverOption) {
88268826
}
88278827

88288828
@Override
8829+
@Deprecated
88298830
public List<Object> clusterSlots() {
88308831
checkIsInMultiOrPipeline();
88318832
connection.sendCommand(CLUSTER, ClusterKeyword.SLOTS);
88328833
return connection.getObjectMultiBulkReply();
88338834
}
88348835

8836+
@Override
8837+
public List<ClusterShardInfo> clusterShards() {
8838+
checkIsInMultiOrPipeline();
8839+
connection.sendCommand(CLUSTER, ClusterKeyword.SHARDS);
8840+
return BuilderFactory.CLUSTER_SHARD_INFO_LIST.build(connection.getObjectMultiBulkReply());
8841+
}
8842+
88358843
@Override
88368844
public String clusterMyId() {
88378845
checkIsInMultiOrPipeline();

src/main/java/redis/clients/jedis/Protocol.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ public static enum ClusterKeyword implements Rawable {
357357
MEET, RESET, INFO, FAILOVER, SLOTS, NODES, REPLICAS, SLAVES, MYID, ADDSLOTS, DELSLOTS,
358358
GETKEYSINSLOT, SETSLOT, NODE, MIGRATING, IMPORTING, STABLE, FORGET, FLUSHSLOTS, KEYSLOT,
359359
COUNTKEYSINSLOT, SAVECONFIG, REPLICATE, LINKS, ADDSLOTSRANGE, DELSLOTSRANGE, BUMPEPOCH,
360-
MYSHARDID;
360+
MYSHARDID, SHARDS;
361361

362362
private final byte[] raw;
363363

src/main/java/redis/clients/jedis/commands/ClusterCommands.java

+17
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import redis.clients.jedis.args.ClusterResetType;
77
import redis.clients.jedis.args.ClusterFailoverOption;
8+
import redis.clients.jedis.resps.ClusterShardInfo;
89

910
public interface ClusterCommands {
1011

@@ -79,8 +80,24 @@ public interface ClusterCommands {
7980

8081
String clusterFailover(ClusterFailoverOption failoverOption);
8182

83+
/**
84+
* {@code CLUSTER SLOTS} command is deprecated since Redis 7.
85+
*
86+
* @deprecated Use {@link ClusterCommands#clusterShards()}.
87+
*/
88+
@Deprecated
8289
List<Object> clusterSlots();
8390

91+
/**
92+
* {@code CLUSTER SHARDS} returns details about the shards of the cluster.
93+
* This command replaces the {@code CLUSTER SLOTS} command from Redis 7,
94+
* by providing a more efficient and extensible representation of the cluster.
95+
*
96+
* @return a list of shards, with each shard containing two objects, 'slots' and 'nodes'.
97+
* @see <a href="https://redis.io/commands/cluster-shards/">CLUSTER SHARDS</a>
98+
*/
99+
List<ClusterShardInfo> clusterShards();
100+
84101
String clusterReset();
85102

86103
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package redis.clients.jedis.resps;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
6+
/**
7+
* This class holds information about a shard of the cluster with command {@code CLUSTER SHARDS}.
8+
* They can be accessed via getters. There is also {@link ClusterShardInfo#getClusterShardInfo()}
9+
* method that returns a generic {@link Map} in case more info are returned from the server.
10+
*/
11+
public class ClusterShardInfo {
12+
13+
public static final String SLOTS = "slots";
14+
public static final String NODES = "nodes";
15+
16+
private final List<List<Long>> slots;
17+
private final List<ClusterShardNodeInfo> nodes;
18+
19+
private final Map<String, Object> clusterShardInfo;
20+
21+
/**
22+
* @param map contains key-value pairs with cluster shard info
23+
*/
24+
@SuppressWarnings("unchecked")
25+
public ClusterShardInfo(Map<String, Object> map) {
26+
slots = (List<List<Long>>) map.get(SLOTS);
27+
nodes = (List<ClusterShardNodeInfo>) map.get(NODES);
28+
29+
clusterShardInfo = map;
30+
}
31+
32+
public List<List<Long>> getSlots() {
33+
return slots;
34+
}
35+
36+
public List<ClusterShardNodeInfo> getNodes() {
37+
return nodes;
38+
}
39+
40+
public Map<String, Object> getClusterShardInfo() {
41+
return clusterShardInfo;
42+
}
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package redis.clients.jedis.resps;
2+
3+
import java.util.Map;
4+
5+
/**
6+
* This class holds information about a node of the cluster with command {@code CLUSTER SHARDS}.
7+
* They can be accessed via getters. There is also {@link ClusterShardNodeInfo#getClusterShardNodeInfo()}
8+
* method that returns a generic {@link Map} in case more info are returned from the server.
9+
*/
10+
public class ClusterShardNodeInfo {
11+
12+
public static final String ID = "id";
13+
public static final String ENDPOINT = "endpoint";
14+
public static final String IP = "ip";
15+
public static final String HOSTNAME = "hostname";
16+
public static final String PORT = "port";
17+
public static final String TLS_PORT = "tls-port";
18+
public static final String ROLE = "role";
19+
public static final String REPLICATION_OFFSET = "replication-offset";
20+
public static final String HEALTH = "health";
21+
22+
private final String id;
23+
private final String endpoint;
24+
private final String ip;
25+
private final String hostname;
26+
private final Long port;
27+
private final Long tlsPort;
28+
private final String role;
29+
private final Long replicationOffset;
30+
private final String health;
31+
32+
private final Map<String, Object> clusterShardNodeInfo;
33+
34+
/**
35+
* @param map contains key-value pairs with node info
36+
*/
37+
public ClusterShardNodeInfo(Map<String, Object> map) {
38+
id = (String) map.get(ID);
39+
endpoint = (String) map.get(ENDPOINT);
40+
ip = (String) map.get(IP);
41+
hostname = (String) map.get(HOSTNAME);
42+
port = (Long) map.get(PORT);
43+
tlsPort = (Long) map.get(TLS_PORT);
44+
role = (String) map.get(ROLE);
45+
replicationOffset = (Long) map.get(REPLICATION_OFFSET);
46+
health = (String) map.get(HEALTH);
47+
48+
clusterShardNodeInfo = map;
49+
}
50+
51+
public String getId() {
52+
return id;
53+
}
54+
55+
public String getEndpoint() {
56+
return endpoint;
57+
}
58+
59+
public String getIp() {
60+
return ip;
61+
}
62+
63+
public String getHostname() {
64+
return hostname;
65+
}
66+
67+
public Long getPort() {
68+
return port;
69+
}
70+
71+
public Long getTlsPort() {
72+
return tlsPort;
73+
}
74+
75+
public String getRole() {
76+
return role;
77+
}
78+
79+
public Long getReplicationOffset() {
80+
return replicationOffset;
81+
}
82+
83+
public String getHealth() {
84+
return health;
85+
}
86+
87+
public Map<String, Object> getClusterShardNodeInfo() {
88+
return clusterShardNodeInfo;
89+
}
90+
91+
public boolean isSsl() {
92+
return tlsPort != null;
93+
}
94+
}

0 commit comments

Comments
 (0)