Skip to content

Commit ba3ff9f

Browse files
committed
DATAREDIS-698 - First round of polishing.
Eagerly initialize known commands. Preallocate Jedis response builder. Replace list concatenation with array copy. Reject null arguments in execute() Visibility modifiers, Javadoc.
1 parent 5dcb161 commit ba3ff9f

File tree

3 files changed

+82
-44
lines changed

3 files changed

+82
-44
lines changed

Diff for: src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -3047,7 +3047,7 @@ public void openPipeline() {
30473047
*/
30483048
@Override
30493049
public Object execute(String command) {
3050-
return execute(command, (byte[][]) null);
3050+
return execute(command, new byte[0][]);
30513051
}
30523052

30533053
/*

Diff for: src/main/java/org/springframework/data/redis/connection/jedis/JedisClientUtils.java

+74-33
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,33 @@
2929

3030
import java.lang.reflect.Field;
3131
import java.lang.reflect.Method;
32-
import java.util.ArrayList;
32+
import java.util.Arrays;
3333
import java.util.Collection;
34-
import java.util.List;
34+
import java.util.Set;
3535
import java.util.function.Supplier;
36+
import java.util.stream.Collectors;
3637

3738
import org.springframework.beans.DirectFieldAccessor;
3839
import org.springframework.lang.Nullable;
3940
import org.springframework.util.ClassUtils;
4041
import org.springframework.util.ReflectionUtils;
4142

4243
/**
44+
* Utility class to invoke arbitraty Jedis commands.
45+
*
4346
* @author Christoph Strobl
47+
* @author Mark Paluch
4448
* @since 2.1
4549
*/
50+
@SuppressWarnings({ "unchecked", "ConstantConditions" })
4651
class JedisClientUtils {
4752

4853
private static final Field CLIENT_FIELD;
4954
private static final Method SEND_COMMAND;
5055
private static final Method GET_RESPONSE;
5156
private static final Method PROTOCOL_SEND_COMMAND;
57+
private static final Set<String> KNOWN_COMMANDS;
58+
private static final Builder<Object> OBJECT_BUILDER;
5259

5360
static {
5461

@@ -60,12 +67,12 @@ class JedisClientUtils {
6067
ReflectionUtils.makeAccessible(PROTOCOL_SEND_COMMAND);
6168

6269
try {
70+
6371
Class<?> commandType = ClassUtils.isPresent("redis.clients.jedis.ProtocolCommand", null)
6472
? ClassUtils.forName("redis.clients.jedis.ProtocolCommand", null)
6573
: ClassUtils.forName("redis.clients.jedis.Protocol$Command", null);
6674

67-
SEND_COMMAND = ReflectionUtils.findMethod(Connection.class, "sendCommand",
68-
new Class[] { commandType, byte[][].class });
75+
SEND_COMMAND = ReflectionUtils.findMethod(Connection.class, "sendCommand", commandType, byte[][].class);
6976
} catch (Exception e) {
7077
throw new NoClassDefFoundError(
7178
"Could not find required flavor of command required by 'redis.clients.jedis.Connection#sendCommand'.");
@@ -75,24 +82,64 @@ class JedisClientUtils {
7582

7683
GET_RESPONSE = ReflectionUtils.findMethod(Queable.class, "getResponse", Builder.class);
7784
ReflectionUtils.makeAccessible(GET_RESPONSE);
85+
86+
KNOWN_COMMANDS = Arrays.stream(Command.values()).map(Enum::name).collect(Collectors.toSet());
87+
88+
OBJECT_BUILDER = new Builder<Object>() {
89+
public Object build(Object data) {
90+
return data;
91+
}
92+
93+
public String toString() {
94+
return "Object";
95+
}
96+
};
7897
}
7998

80-
@Nullable
99+
/**
100+
* Execute an arbitrary on the supplied {@link Jedis} instance.
101+
*
102+
* @param command the command.
103+
* @param keys must not be {@literal null}, may be empty.
104+
* @param args must not be {@literal null}, may be empty.
105+
* @param jedis must not be {@literal null}.
106+
* @return the response, can be be {@literal null}.
107+
*/
108+
@Nullable // TODO: Change keys and args to byte[][]?
81109
static <T> T execute(String command, Collection<byte[]> keys, Collection<byte[]> args, Supplier<Jedis> jedis) {
82110

83-
List<byte[]> mArgs = new ArrayList<>(keys);
84-
mArgs.addAll(args);
111+
byte[][] commandArgs = getCommandArguments(keys, args);
85112

86113
Client client = retrieveClient(jedis.get());
87-
sendCommand(client, command, mArgs.toArray(new byte[mArgs.size()][]));
114+
sendCommand(client, command, commandArgs);
88115

89116
return (T) client.getOne();
90117
}
91118

92-
static Client retrieveClient(Jedis jedis) {
93-
return (Client) ReflectionUtils.getField(CLIENT_FIELD, jedis);
119+
private static byte[][] getCommandArguments(Collection<byte[]> keys, Collection<byte[]> args) {
120+
121+
byte[][] commandArgs = new byte[keys.size() + args.size()][];
122+
123+
int index = 0;
124+
for (byte[] key : keys) {
125+
commandArgs[index++] = key;
126+
}
127+
128+
for (byte[] arg : args) {
129+
commandArgs[index++] = arg;
130+
}
131+
132+
return commandArgs;
94133
}
95134

135+
/**
136+
* Send a Redis command and retrieve the {@link Client} for response retrieval.
137+
*
138+
* @param jedis
139+
* @param command
140+
* @param args
141+
* @return
142+
*/
96143
static Client sendCommand(Jedis jedis, String command, byte[][] args) {
97144

98145
Client client = retrieveClient(jedis);
@@ -106,7 +153,7 @@ static Client sendCommand(Jedis jedis, String command, byte[][] args) {
106153
return client;
107154
}
108155

109-
static void sendCommand(Client client, String command, byte[][] args) {
156+
private static void sendCommand(Client client, String command, byte[][] args) {
110157

111158
if (isKnownCommand(command)) {
112159
ReflectionUtils.invokeMethod(SEND_COMMAND, client, Command.valueOf(command.trim().toUpperCase()), args);
@@ -115,8 +162,9 @@ static void sendCommand(Client client, String command, byte[][] args) {
115162
}
116163
}
117164

118-
static void sendProtocolCommand(Client client, String command, byte[][] args) {
165+
private static void sendProtocolCommand(Client client, String command, byte[][] args) {
119166

167+
// quite expensive to construct for each command invocation
120168
DirectFieldAccessor dfa = new DirectFieldAccessor(client);
121169

122170
client.connect();
@@ -125,34 +173,27 @@ static void sendProtocolCommand(Client client, String command, byte[][] args) {
125173
ReflectionUtils.invokeMethod(PROTOCOL_SEND_COMMAND, null, os, SafeEncoder.encode(command), args);
126174

127175
Integer pipelinedCommands = (Integer) dfa.getPropertyValue("pipelinedCommands");
128-
dfa.setPropertyValue("pipelinedCommands", pipelinedCommands.intValue() + 1);
129-
}
130-
131-
static boolean isKnownCommand(String command) {
132-
133-
try {
134-
Command.valueOf(command);
135-
return true;
136-
} catch (IllegalArgumentException e) {
137-
return false;
138-
}
176+
dfa.setPropertyValue("pipelinedCommands", pipelinedCommands + 1);
139177
}
140178

179+
/**
180+
* @param jedis the client instance.
181+
* @return {@literal true} if the connection has entered {@literal MULTI} state.
182+
*/
141183
static boolean isInMulti(Jedis jedis) {
142184
return retrieveClient(jedis).isInMulti();
143185
}
144186

145-
static Response<Object> getGetResponse(Object target) {
146-
147-
return (Response<Object>) ReflectionUtils.invokeMethod(GET_RESPONSE, target, new Builder<Object>() {
148-
public Object build(Object data) {
149-
return data;
150-
}
187+
private static boolean isKnownCommand(String command) {
188+
return KNOWN_COMMANDS.contains(command);
189+
}
151190

152-
public String toString() {
153-
return "Object";
154-
}
155-
});
191+
@SuppressWarnings({ "unchecked", "ConstantConditions" })
192+
static Response<Object> getGetResponse(Object target) {
193+
return (Response<Object>) ReflectionUtils.invokeMethod(GET_RESPONSE, target, OBJECT_BUILDER);
156194
}
157195

196+
private static Client retrieveClient(Jedis jedis) {
197+
return (Client) ReflectionUtils.getField(CLIENT_FIELD, jedis);
198+
}
158199
}

Diff for: src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java

+7-10
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import org.springframework.lang.Nullable;
4343
import org.springframework.util.Assert;
4444
import org.springframework.util.CollectionUtils;
45-
import org.springframework.util.ObjectUtils;
4645
import org.springframework.util.StringUtils;
4746

4847
/**
@@ -273,20 +272,18 @@ public RedisHyperLogLogCommands hyperLogLogCommands() {
273272
*/
274273
@Override
275274
public Object execute(String command, byte[]... args) {
276-
Assert.hasText(command, "a valid command needs to be specified");
275+
276+
Assert.hasText(command, "A valid command needs to be specified!");
277+
Assert.notNull(args, "Arguments must not be null!");
278+
277279
try {
278-
List<byte[]> mArgs = new ArrayList<>();
279-
if (!ObjectUtils.isEmpty(args)) {
280-
Collections.addAll(mArgs, args);
281-
}
282280

283-
Client client = JedisClientUtils.sendCommand(this.jedis, command, mArgs.toArray(new byte[mArgs.size()][]));
281+
Client client = JedisClientUtils.sendCommand(this.jedis, command, args);
284282

285283
if (isQueueing() || isPipelined()) {
286-
Object target = (isPipelined() ? pipeline : transaction);
287-
@SuppressWarnings("unchecked")
288284

289-
Response<Object> result = JedisClientUtils.getGetResponse(target);
285+
Response<Object> result = JedisClientUtils
286+
.getGetResponse(isPipelined() ? getRequiredPipeline() : getRequiredTransaction());
290287
if (isPipelined()) {
291288
pipeline(new JedisResult(result));
292289
} else {

0 commit comments

Comments
 (0)