Skip to content

Commit 35505f1

Browse files
sazzad16R-J Lim
and
R-J Lim
authored
Support automatic namespacing (#3781)
* Proof-of-concept for automatic key prefixing * Iteration on key-prefixing POC - Demonstrated automatic key-prefixing for all subclasses of UnifiedJedis: JedisCluster, JedisPooled, and JedisSentineled - Key-prefixing is possible as long as the underlying CommandObjects can be customized. - CommandObjects cannot use commandArguments in its constructor since in the specific case of key-prefixing, commandArguments depends on the child constructor running first. So we lose caching of argument-less CommandObjects. - Based on this POC, the minimum changes required to jedis would be: - public constructors that allow UnifiedJedis and its subclasses to take a custom CommandObjects. - Consistent use of supplied CommandObjects throughout code (e.g. in Pipeline, Transaction, etc). - Removal of caching of argument-less CommandObjects in the constructor of CommandObjects. - Applications can then supply CommandObjects with custom behavior as necessary. Sample classes that implement the behavior of prefixed keys, etc are provided but these can be supplied by the application as long as required constructors are available. * Second iteration on key-prefixing POC - Restore cached key-less commands in CommandObjects - Support Transactions - New constructors do not take CommandExecutor - Requested JavaDoc regarding new constructors specifying RedisProtocol - New classes moved into 'prefix' packages - De-duplicate prefixing code * Support automatic key prefixing by handler interface --------- Co-authored-by: R-J Lim <[email protected]>
1 parent 5570879 commit 35505f1

17 files changed

+316
-28
lines changed

Diff for: src/main/java/redis/clients/jedis/AbstractTransaction.java

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

66
public abstract class AbstractTransaction extends PipeliningBase implements Closeable {
77

8+
@Deprecated
89
protected AbstractTransaction() {
910
super(new CommandObjects());
1011
}

Diff for: src/main/java/redis/clients/jedis/ClusterCommandObjects.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ public class ClusterCommandObjects extends CommandObjects {
1616

1717
@Override
1818
protected ClusterCommandArguments commandArguments(ProtocolCommand command) {
19-
return new ClusterCommandArguments(command);
19+
ClusterCommandArguments comArgs = new ClusterCommandArguments(command);
20+
if (keyPreProcessor != null) comArgs.setKeyArgumentPreProcessor(keyPreProcessor);
21+
return comArgs;
2022
}
2123

2224
private static final String CLUSTER_UNSUPPORTED_MESSAGE = "Not supported in cluster mode.";

Diff for: src/main/java/redis/clients/jedis/CommandArguments.java

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.Collection;
66
import java.util.Iterator;
77

8+
import redis.clients.jedis.annots.Experimental;
89
import redis.clients.jedis.args.Rawable;
910
import redis.clients.jedis.args.RawableFactory;
1011
import redis.clients.jedis.commands.ProtocolCommand;
@@ -13,6 +14,7 @@
1314

1415
public class CommandArguments implements Iterable<Rawable> {
1516

17+
private CommandKeyArgumentPreProcessor keyPreProc = null;
1618
private final ArrayList<Rawable> args;
1719

1820
private boolean blocking;
@@ -30,6 +32,11 @@ public ProtocolCommand getCommand() {
3032
return (ProtocolCommand) args.get(0);
3133
}
3234

35+
@Experimental
36+
void setKeyArgumentPreProcessor(CommandKeyArgumentPreProcessor keyPreProcessor) {
37+
this.keyPreProc = keyPreProcessor;
38+
}
39+
3340
public CommandArguments add(Rawable arg) {
3441
args.add(arg);
3542
return this;
@@ -100,6 +107,10 @@ public CommandArguments addObjects(Collection args) {
100107
}
101108

102109
public CommandArguments key(Object key) {
110+
if (keyPreProc != null) {
111+
key = keyPreProc.actualKey(key);
112+
}
113+
103114
if (key instanceof Rawable) {
104115
Rawable raw = (Rawable) key;
105116
processKey(raw.getRaw());
@@ -115,6 +126,7 @@ public CommandArguments key(Object key) {
115126
} else {
116127
throw new IllegalArgumentException("\"" + key.toString() + "\" is not a valid argument.");
117128
}
129+
118130
return this;
119131
}
120132

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package redis.clients.jedis;
2+
3+
import redis.clients.jedis.annots.Experimental;
4+
5+
@Experimental
6+
public interface CommandKeyArgumentPreProcessor {
7+
8+
/**
9+
* @param paramKey key name in application
10+
* @return key name in Redis server
11+
*/
12+
Object actualKey(Object paramKey);
13+
}

Diff for: src/main/java/redis/clients/jedis/CommandObjects.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import redis.clients.jedis.Protocol.Command;
1616
import redis.clients.jedis.Protocol.Keyword;
17+
import redis.clients.jedis.annots.Experimental;
1718
import redis.clients.jedis.args.*;
1819
import redis.clients.jedis.bloom.*;
1920
import redis.clients.jedis.bloom.RedisBloomProtocol.*;
@@ -52,18 +53,25 @@ protected RedisProtocol getProtocol() {
5253
return protocol;
5354
}
5455

56+
protected volatile CommandKeyArgumentPreProcessor keyPreProcessor = null;
57+
private JedisBroadcastAndRoundRobinConfig broadcastAndRoundRobinConfig = null;
5558
private Lock mapperLock = new ReentrantLock(true);
5659
private volatile JsonObjectMapper jsonObjectMapper;
5760
private final AtomicInteger searchDialect = new AtomicInteger(0);
5861

59-
private JedisBroadcastAndRoundRobinConfig broadcastAndRoundRobinConfig = null;
62+
@Experimental
63+
void setKeyArgumentPreProcessor(CommandKeyArgumentPreProcessor keyPreProcessor) {
64+
this.keyPreProcessor = keyPreProcessor;
65+
}
6066

6167
void setBroadcastAndRoundRobinConfig(JedisBroadcastAndRoundRobinConfig config) {
6268
this.broadcastAndRoundRobinConfig = config;
6369
}
6470

6571
protected CommandArguments commandArguments(ProtocolCommand command) {
66-
return new CommandArguments(command);
72+
CommandArguments comArgs = new CommandArguments(command);
73+
if (keyPreProcessor != null) comArgs.setKeyArgumentPreProcessor(keyPreProcessor);
74+
return comArgs;
6775
}
6876

6977
private final CommandObject<String> PING_COMMAND_OBJECT = new CommandObject<>(commandArguments(PING), BuilderFactory.STRING);
@@ -4424,6 +4432,16 @@ public final CommandObject<Object> tFunctionCallAsync(String library, String fun
44244432
}
44254433
// RedisGears commands
44264434

4435+
// Transaction commands
4436+
public final CommandObject<String> watch(String... keys) {
4437+
return new CommandObject<>(commandArguments(WATCH).keys((Object[]) keys), BuilderFactory.STRING);
4438+
}
4439+
4440+
public final CommandObject<String> watch(byte[]... keys) {
4441+
return new CommandObject<>(commandArguments(WATCH).keys((Object[]) keys), BuilderFactory.STRING);
4442+
}
4443+
// Transaction commands
4444+
44274445
/**
44284446
* Get the instance for JsonObjectMapper if not null, otherwise a new instance reference with
44294447
* default implementation will be created and returned.

Diff for: src/main/java/redis/clients/jedis/Pipeline.java

+15-4
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,23 @@ public Pipeline(Connection connection) {
2929
}
3030

3131
public Pipeline(Connection connection, boolean closeConnection) {
32-
super(new CommandObjects());
32+
this(connection, closeConnection, createCommandObjects(connection));
33+
}
34+
35+
private static CommandObjects createCommandObjects(Connection connection) {
36+
CommandObjects commandObjects = new CommandObjects();
37+
RedisProtocol proto = connection.getRedisProtocol();
38+
if (proto != null) commandObjects.setProtocol(proto);
39+
return commandObjects;
40+
}
41+
42+
Pipeline(Connection connection, boolean closeConnection, CommandObjects commandObjects) {
43+
super(commandObjects);
3344
this.connection = connection;
3445
this.closeConnection = closeConnection;
35-
RedisProtocol proto = this.connection.getRedisProtocol();
36-
if (proto != null) this.commandObjects.setProtocol(proto);
37-
setGraphCommands(new GraphCommandObjects(this.connection));
46+
GraphCommandObjects graphCommandObjects = new GraphCommandObjects(this.connection);
47+
graphCommandObjects.setBaseCommandArgumentsCreator(protocolCommand -> commandObjects.commandArguments(protocolCommand));
48+
setGraphCommands(graphCommandObjects);
3849
}
3950

4051
@Override

Diff for: src/main/java/redis/clients/jedis/ReliableTransaction.java

+29-8
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import static redis.clients.jedis.Protocol.Command.EXEC;
55
import static redis.clients.jedis.Protocol.Command.MULTI;
66
import static redis.clients.jedis.Protocol.Command.UNWATCH;
7-
import static redis.clients.jedis.Protocol.Command.WATCH;
87

98
import java.util.ArrayList;
109
import java.util.LinkedList;
@@ -17,8 +16,7 @@
1716
import redis.clients.jedis.graph.GraphCommandObjects;
1817

1918
/**
20-
* ReliableTransaction is a transaction where commands are immediately sent to Redis server and the
21-
* 'QUEUED' reply checked.
19+
* A transaction where commands are immediately sent to Redis server and the {@code QUEUED} reply checked.
2220
*/
2321
public class ReliableTransaction extends TransactionBase {
2422

@@ -66,12 +64,37 @@ public ReliableTransaction(Connection connection, boolean doMulti) {
6664
* @param closeConnection should the 'connection' be closed when 'close()' is called?
6765
*/
6866
public ReliableTransaction(Connection connection, boolean doMulti, boolean closeConnection) {
67+
this(connection, doMulti, closeConnection, createCommandObjects(connection));
68+
}
69+
70+
/**
71+
* Creates a new transaction.
72+
*
73+
* A user wanting to WATCH/UNWATCH keys followed by a call to MULTI ({@link #multi()}) it should
74+
* be {@code doMulti=false}.
75+
*
76+
* @param connection connection
77+
* @param commandObjects command objects
78+
* @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI
79+
* @param closeConnection should the 'connection' be closed when 'close()' is called?
80+
*/
81+
ReliableTransaction(Connection connection, boolean doMulti, boolean closeConnection, CommandObjects commandObjects) {
82+
super(commandObjects);
6983
this.connection = connection;
7084
this.closeConnection = closeConnection;
71-
setGraphCommands(new GraphCommandObjects(this.connection));
85+
GraphCommandObjects graphCommandObjects = new GraphCommandObjects(this.connection);
86+
graphCommandObjects.setBaseCommandArgumentsCreator(protocolCommand -> commandObjects.commandArguments(protocolCommand));
87+
setGraphCommands(graphCommandObjects);
7288
if (doMulti) multi();
7389
}
7490

91+
private static CommandObjects createCommandObjects(Connection connection) {
92+
CommandObjects commandObjects = new CommandObjects();
93+
RedisProtocol proto = connection.getRedisProtocol();
94+
if (proto != null) commandObjects.setProtocol(proto);
95+
return commandObjects;
96+
}
97+
7598
@Override
7699
public final void multi() {
77100
connection.sendCommand(MULTI);
@@ -84,16 +107,14 @@ public final void multi() {
84107

85108
@Override
86109
public String watch(final String... keys) {
87-
connection.sendCommand(WATCH, keys);
88-
String status = connection.getStatusCodeReply();
110+
String status = connection.executeCommand(commandObjects.watch(keys));
89111
inWatch = true;
90112
return status;
91113
}
92114

93115
@Override
94116
public String watch(final byte[]... keys) {
95-
connection.sendCommand(WATCH, keys);
96-
String status = connection.getStatusCodeReply();
117+
String status = connection.executeCommand(commandObjects.watch(keys));
97118
inWatch = true;
98119
return status;
99120
}

Diff for: src/main/java/redis/clients/jedis/ShardedCommandObjects.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ public ShardedCommandObjects(Hashing algo, Pattern tagPattern) {
3434

3535
@Override
3636
protected ShardedCommandArguments commandArguments(ProtocolCommand command) {
37-
return new ShardedCommandArguments(algo, tagPattern, command);
37+
ShardedCommandArguments comArgs = new ShardedCommandArguments(algo, tagPattern, command);
38+
if (keyPreProcessor != null) comArgs.setKeyArgumentPreProcessor(keyPreProcessor);
39+
return comArgs;
3840
}
3941

4042
@Override

Diff for: src/main/java/redis/clients/jedis/Transaction.java

+30-8
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import static redis.clients.jedis.Protocol.Command.EXEC;
55
import static redis.clients.jedis.Protocol.Command.MULTI;
66
import static redis.clients.jedis.Protocol.Command.UNWATCH;
7-
import static redis.clients.jedis.Protocol.Command.WATCH;
87

98
import java.util.ArrayList;
109
import java.util.LinkedList;
@@ -16,7 +15,7 @@
1615
import redis.clients.jedis.graph.GraphCommandObjects;
1716

1817
/**
19-
* A pipeline based transaction.
18+
* A transaction based on <a href="https://redis.io/docs/manual/pipelining/">pipelining</a>.
2019
*/
2120
public class Transaction extends TransactionBase {
2221

@@ -59,7 +58,7 @@ public Transaction(Connection connection) {
5958
* @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI
6059
*/
6160
public Transaction(Connection connection, boolean doMulti) {
62-
this(connection, doMulti, false);
61+
this(connection, doMulti, false, createCommandObjects(connection));
6362
}
6463

6564
/**
@@ -73,12 +72,37 @@ public Transaction(Connection connection, boolean doMulti) {
7372
* @param closeConnection should the 'connection' be closed when 'close()' is called?
7473
*/
7574
public Transaction(Connection connection, boolean doMulti, boolean closeConnection) {
75+
this(connection, doMulti, closeConnection, createCommandObjects(connection));
76+
}
77+
78+
/**
79+
* Creates a new transaction.
80+
*
81+
* A user wanting to WATCH/UNWATCH keys followed by a call to MULTI ({@link #multi()}) it should
82+
* be {@code doMulti=false}.
83+
*
84+
* @param connection connection
85+
* @param commandObjects command objects
86+
* @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI
87+
* @param closeConnection should the 'connection' be closed when 'close()' is called?
88+
*/
89+
Transaction(Connection connection, boolean doMulti, boolean closeConnection, CommandObjects commandObjects) {
90+
super(commandObjects);
7691
this.connection = connection;
7792
this.closeConnection = closeConnection;
78-
setGraphCommands(new GraphCommandObjects(this.connection));
93+
GraphCommandObjects graphCommandObjects = new GraphCommandObjects(this.connection);
94+
graphCommandObjects.setBaseCommandArgumentsCreator(protocolCommand -> commandObjects.commandArguments(protocolCommand));
95+
setGraphCommands(graphCommandObjects);
7996
if (doMulti) multi();
8097
}
8198

99+
private static CommandObjects createCommandObjects(Connection connection) {
100+
CommandObjects commandObjects = new CommandObjects();
101+
RedisProtocol proto = connection.getRedisProtocol();
102+
if (proto != null) commandObjects.setProtocol(proto);
103+
return commandObjects;
104+
}
105+
82106
@Override
83107
public final void multi() {
84108
connection.sendCommand(MULTI);
@@ -88,16 +112,14 @@ public final void multi() {
88112

89113
@Override
90114
public String watch(final String... keys) {
91-
connection.sendCommand(WATCH, keys);
92-
String status = connection.getStatusCodeReply();
115+
String status = connection.executeCommand(commandObjects.watch(keys));
93116
inWatch = true;
94117
return status;
95118
}
96119

97120
@Override
98121
public String watch(final byte[]... keys) {
99-
connection.sendCommand(WATCH, keys);
100-
String status = connection.getStatusCodeReply();
122+
String status = connection.executeCommand(commandObjects.watch(keys));
101123
inWatch = true;
102124
return status;
103125
}

Diff for: src/main/java/redis/clients/jedis/TransactionBase.java

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@Deprecated
77
public abstract class TransactionBase extends AbstractTransaction {
88

9+
@Deprecated
910
protected TransactionBase() {
1011
super();
1112
}

Diff for: src/main/java/redis/clients/jedis/UnifiedJedis.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -5019,7 +5019,7 @@ public PipelineBase pipelined() {
50195019
} else if (provider instanceof MultiClusterPooledConnectionProvider) {
50205020
return new MultiClusterPipeline((MultiClusterPooledConnectionProvider) provider, commandObjects);
50215021
} else {
5022-
return new Pipeline(provider.getConnection(), true);
5022+
return new Pipeline(provider.getConnection(), true, commandObjects);
50235023
}
50245024
}
50255025

@@ -5040,7 +5040,7 @@ public AbstractTransaction transaction(boolean doMulti) {
50405040
} else if (provider instanceof MultiClusterPooledConnectionProvider) {
50415041
return new MultiClusterTransaction((MultiClusterPooledConnectionProvider) provider, doMulti, commandObjects);
50425042
} else {
5043-
return new Transaction(provider.getConnection(), doMulti, true);
5043+
return new Transaction(provider.getConnection(), doMulti, true, commandObjects);
50445044
}
50455045
}
50465046

@@ -5084,6 +5084,11 @@ public Object executeCommand(CommandArguments args) {
50845084
return executeCommand(new CommandObject<>(args, BuilderFactory.RAW_OBJECT));
50855085
}
50865086

5087+
@Experimental
5088+
public void setKeyArgumentPreProcessor(CommandKeyArgumentPreProcessor keyPreProcessor) {
5089+
this.commandObjects.setKeyArgumentPreProcessor(keyPreProcessor);
5090+
}
5091+
50875092
public void setJsonObjectMapper(JsonObjectMapper jsonObjectMapper) {
50885093
this.commandObjects.setJsonObjectMapper(jsonObjectMapper);
50895094
}

Diff for: src/main/java/redis/clients/jedis/mcf/MultiClusterTransaction.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public final void multi() {
9494
*/
9595
@Override
9696
public final String watch(String... keys) {
97-
appendCommand(new CommandObject<>(new CommandArguments(WATCH).addObjects((Object[]) keys), NO_OP_BUILDER));
97+
appendCommand(commandObjects.watch(keys));
9898
extraCommandCount.incrementAndGet();
9999
inWatch = true;
100100
return null;
@@ -106,7 +106,7 @@ public final String watch(String... keys) {
106106
*/
107107
@Override
108108
public final String watch(byte[]... keys) {
109-
appendCommand(new CommandObject<>(new CommandArguments(WATCH).addObjects((Object[]) keys), NO_OP_BUILDER));
109+
appendCommand(commandObjects.watch(keys));
110110
extraCommandCount.incrementAndGet();
111111
inWatch = true;
112112
return null;

0 commit comments

Comments
 (0)