Skip to content

Commit 3ab6bdc

Browse files
authored
Support client-side caching from UnifiedJedis (#3691)
* Support client side caching from UnifiedJedis * Support client side caching as a separate parameter * format imports * Support CSC in sentinel mode * undo change
1 parent d87fc6e commit 3ab6bdc

21 files changed

+572
-239
lines changed

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

+17-4
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,27 @@
1010

1111
public class ClientSideCache {
1212

13-
private final Map<ByteBuffer, Object> cache = new HashMap<>();
13+
private final Map<ByteBuffer, Object> cache;
1414

15-
protected ClientSideCache() {
15+
public ClientSideCache() {
16+
this.cache = new HashMap<>();
1617
}
1718

18-
protected void invalidateKeys(List list) {
19+
/**
20+
* For testing purpose only.
21+
* @param map
22+
*/
23+
ClientSideCache(Map<ByteBuffer, Object> map) {
24+
this.cache = map;
25+
}
26+
27+
public final void clear() {
28+
cache.clear();
29+
}
30+
31+
public final void invalidateKeys(List list) {
1932
if (list == null) {
20-
cache.clear();
33+
clear();
2134
return;
2235
}
2336

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

+26-9
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,7 @@ public Connection(final HostAndPort hostAndPort) {
5252
}
5353

5454
public Connection(final HostAndPort hostAndPort, final JedisClientConfig clientConfig) {
55-
this(new DefaultJedisSocketFactory(hostAndPort, clientConfig));
56-
this.infiniteSoTimeout = clientConfig.getBlockingSocketTimeoutMillis();
57-
initializeFromClientConfig(clientConfig);
55+
this(new DefaultJedisSocketFactory(hostAndPort, clientConfig), clientConfig);
5856
}
5957

6058
public Connection(final JedisSocketFactory socketFactory) {
@@ -65,7 +63,15 @@ public Connection(final JedisSocketFactory socketFactory, JedisClientConfig clie
6563
this.socketFactory = socketFactory;
6664
this.soTimeout = clientConfig.getSocketTimeoutMillis();
6765
this.infiniteSoTimeout = clientConfig.getBlockingSocketTimeoutMillis();
68-
initializeFromClientConfig(clientConfig);
66+
initializeConnection(clientConfig);
67+
}
68+
69+
public Connection(final JedisSocketFactory socketFactory, JedisClientConfig clientConfig, ClientSideCache csCache) {
70+
this.socketFactory = socketFactory;
71+
this.soTimeout = clientConfig.getSocketTimeoutMillis();
72+
this.infiniteSoTimeout = clientConfig.getBlockingSocketTimeoutMillis();
73+
initializeConnection(clientConfig);
74+
initializeClientSideCache(csCache);
6975
}
7076

7177
@Override
@@ -122,10 +128,6 @@ public void rollbackTimeout() {
122128
}
123129
}
124130

125-
final void setClientSideCache(ClientSideCache clientSideCache) {
126-
this.clientSideCache = clientSideCache;
127-
}
128-
129131
public Object executeCommand(final ProtocolCommand cmd) {
130132
return executeCommand(new CommandArguments(cmd));
131133
}
@@ -389,7 +391,7 @@ private static boolean validateClientInfo(String info) {
389391
return true;
390392
}
391393

392-
private void initializeFromClientConfig(final JedisClientConfig config) {
394+
private void initializeConnection(final JedisClientConfig config) {
393395
try {
394396
connect();
395397

@@ -516,4 +518,19 @@ public boolean ping() {
516518
}
517519
return true;
518520
}
521+
522+
private void initializeClientSideCache(ClientSideCache csCache) {
523+
this.clientSideCache = csCache;
524+
if (clientSideCache != null) {
525+
if (protocol != RedisProtocol.RESP3) {
526+
throw new JedisException("Client side caching is only supported with RESP3.");
527+
}
528+
529+
sendCommand(Protocol.Command.CLIENT, "TRACKING", "ON");
530+
String reply = getStatusCodeReply();
531+
if (!"OK".equals(reply)) {
532+
throw new JedisException("Could not enable client tracking. Reply: " + reply);
533+
}
534+
}
535+
}
519536
}

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

+13-5
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,27 @@ public class ConnectionFactory implements PooledObjectFactory<Connection> {
1717
private static final Logger logger = LoggerFactory.getLogger(ConnectionFactory.class);
1818

1919
private final JedisSocketFactory jedisSocketFactory;
20-
2120
private final JedisClientConfig clientConfig;
21+
private ClientSideCache clientSideCache = null;
2222

2323
public ConnectionFactory(final HostAndPort hostAndPort) {
2424
this.clientConfig = DefaultJedisClientConfig.builder().build();
2525
this.jedisSocketFactory = new DefaultJedisSocketFactory(hostAndPort);
2626
}
2727

2828
public ConnectionFactory(final HostAndPort hostAndPort, final JedisClientConfig clientConfig) {
29-
this.clientConfig = DefaultJedisClientConfig.copyConfig(clientConfig);
29+
this.clientConfig = clientConfig;
3030
this.jedisSocketFactory = new DefaultJedisSocketFactory(hostAndPort, this.clientConfig);
3131
}
3232

33+
public ConnectionFactory(final HostAndPort hostAndPort, final JedisClientConfig clientConfig, ClientSideCache csCache) {
34+
this.clientConfig = clientConfig;
35+
this.jedisSocketFactory = new DefaultJedisSocketFactory(hostAndPort, this.clientConfig);
36+
this.clientSideCache = csCache;
37+
}
38+
3339
public ConnectionFactory(final JedisSocketFactory jedisSocketFactory, final JedisClientConfig clientConfig) {
34-
this.clientConfig = DefaultJedisClientConfig.copyConfig(clientConfig);
40+
this.clientConfig = clientConfig;
3541
this.jedisSocketFactory = jedisSocketFactory;
3642
}
3743

@@ -54,9 +60,11 @@ public void destroyObject(PooledObject<Connection> pooledConnection) throws Exce
5460

5561
@Override
5662
public PooledObject<Connection> makeObject() throws Exception {
57-
Connection jedis = null;
5863
try {
59-
jedis = new Connection(jedisSocketFactory, clientConfig);
64+
Connection jedis = clientSideCache == null
65+
? new Connection(jedisSocketFactory, clientConfig)
66+
: new Connection(jedisSocketFactory, clientConfig, clientSideCache);
67+
6068
return new DefaultPooledObject<>(jedis);
6169
} catch (JedisException je) {
6270
logger.debug("Error while makeObject", je);

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

+9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig) {
1010
this(new ConnectionFactory(hostAndPort, clientConfig));
1111
}
1212

13+
public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig, ClientSideCache csCache) {
14+
this(new ConnectionFactory(hostAndPort, clientConfig, csCache));
15+
}
16+
1317
public ConnectionPool(PooledObjectFactory<Connection> factory) {
1418
super(factory);
1519
}
@@ -19,6 +23,11 @@ public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig,
1923
this(new ConnectionFactory(hostAndPort, clientConfig), poolConfig);
2024
}
2125

26+
public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig, ClientSideCache csCache,
27+
GenericObjectPoolConfig<Connection> poolConfig) {
28+
this(new ConnectionFactory(hostAndPort, clientConfig, csCache), poolConfig);
29+
}
30+
2231
public ConnectionPool(PooledObjectFactory<Connection> factory,
2332
GenericObjectPoolConfig<Connection> poolConfig) {
2433
super(factory, poolConfig);

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

-44
This file was deleted.

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

+44-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import java.util.Set;
77

88
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
9-
109
import redis.clients.jedis.providers.ClusterConnectionProvider;
1110
import redis.clients.jedis.util.JedisClusterCRC16;
1211

@@ -198,28 +197,63 @@ public JedisCluster(Set<HostAndPort> clusterNodes, JedisClientConfig clientConfi
198197
Duration.ofMillis((long) clientConfig.getSocketTimeoutMillis() * maxAttempts), poolConfig);
199198
}
200199

200+
public JedisCluster(Set<HostAndPort> clusterNodes, JedisClientConfig clientConfig, int maxAttempts,
201+
Duration maxTotalRetriesDuration, GenericObjectPoolConfig<Connection> poolConfig) {
202+
this(new ClusterConnectionProvider(clusterNodes, clientConfig, poolConfig), maxAttempts, maxTotalRetriesDuration,
203+
clientConfig.getRedisProtocol());
204+
}
205+
201206
public JedisCluster(Set<HostAndPort> clusterNodes, JedisClientConfig clientConfig,
202207
GenericObjectPoolConfig<Connection> poolConfig, Duration topologyRefreshPeriod, int maxAttempts,
203208
Duration maxTotalRetriesDuration) {
204209
this(new ClusterConnectionProvider(clusterNodes, clientConfig, poolConfig, topologyRefreshPeriod),
205210
maxAttempts, maxTotalRetriesDuration, clientConfig.getRedisProtocol());
206211
}
207212

208-
public JedisCluster(Set<HostAndPort> clusterNodes, JedisClientConfig clientConfig, int maxAttempts,
209-
Duration maxTotalRetriesDuration, GenericObjectPoolConfig<Connection> poolConfig) {
210-
this(new ClusterConnectionProvider(clusterNodes, clientConfig, poolConfig), maxAttempts, maxTotalRetriesDuration,
211-
clientConfig.getRedisProtocol());
213+
private JedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration,
214+
RedisProtocol protocol) {
215+
super(provider, maxAttempts, maxTotalRetriesDuration, protocol);
212216
}
213217

214-
// Uses a fetched connection to process protocol. Should be avoided if possible.
215-
public JedisCluster(ClusterConnectionProvider provider, int maxAttempts,
218+
public JedisCluster(Set<HostAndPort> clusterNodes, JedisClientConfig clientConfig, ClientSideCache clientSideCache) {
219+
this(clusterNodes, clientConfig, clientSideCache, DEFAULT_MAX_ATTEMPTS,
220+
Duration.ofMillis(DEFAULT_MAX_ATTEMPTS * clientConfig.getSocketTimeoutMillis()));
221+
}
222+
223+
public JedisCluster(Set<HostAndPort> clusterNodes, JedisClientConfig clientConfig, ClientSideCache clientSideCache,
224+
int maxAttempts, Duration maxTotalRetriesDuration) {
225+
this(new ClusterConnectionProvider(clusterNodes, clientConfig, clientSideCache), maxAttempts, maxTotalRetriesDuration,
226+
clientConfig.getRedisProtocol(), clientSideCache);
227+
}
228+
229+
public JedisCluster(Set<HostAndPort> clusterNodes, JedisClientConfig clientConfig, ClientSideCache clientSideCache,
230+
int maxAttempts, Duration maxTotalRetriesDuration, GenericObjectPoolConfig<Connection> poolConfig) {
231+
this(new ClusterConnectionProvider(clusterNodes, clientConfig, clientSideCache, poolConfig),
232+
maxAttempts, maxTotalRetriesDuration, clientConfig.getRedisProtocol(), clientSideCache);
233+
}
234+
235+
public JedisCluster(Set<HostAndPort> clusterNodes, JedisClientConfig clientConfig, ClientSideCache clientSideCache,
236+
GenericObjectPoolConfig<Connection> poolConfig) {
237+
this(new ClusterConnectionProvider(clusterNodes, clientConfig, clientSideCache, poolConfig),
238+
DEFAULT_MAX_ATTEMPTS, Duration.ofMillis(DEFAULT_MAX_ATTEMPTS * clientConfig.getSocketTimeoutMillis()),
239+
clientConfig.getRedisProtocol(), clientSideCache);
240+
}
241+
242+
public JedisCluster(Set<HostAndPort> clusterNodes, JedisClientConfig clientConfig, ClientSideCache clientSideCache,
243+
GenericObjectPoolConfig<Connection> poolConfig, Duration topologyRefreshPeriod, int maxAttempts,
216244
Duration maxTotalRetriesDuration) {
217-
super(provider, maxAttempts, maxTotalRetriesDuration);
245+
this(new ClusterConnectionProvider(clusterNodes, clientConfig, clientSideCache, poolConfig, topologyRefreshPeriod),
246+
maxAttempts, maxTotalRetriesDuration, clientConfig.getRedisProtocol(), clientSideCache);
218247
}
219248

220249
private JedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration,
221-
RedisProtocol protocol) {
222-
super(provider, maxAttempts, maxTotalRetriesDuration, protocol);
250+
RedisProtocol protocol, ClientSideCache clientSideCache) {
251+
super(provider, maxAttempts, maxTotalRetriesDuration, protocol, clientSideCache);
252+
}
253+
254+
// Uses a fetched connection to process protocol. Should be avoided if possible.
255+
public JedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) {
256+
super(provider, maxAttempts, maxTotalRetriesDuration);
223257
}
224258

225259
public Map<String, ConnectionPool> getClusterNodes() {

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

+39-4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public class JedisClusterInfoCache {
4242

4343
private final GenericObjectPoolConfig<Connection> poolConfig;
4444
private final JedisClientConfig clientConfig;
45+
private final ClientSideCache clientSideCache;
4546
private final Set<HostAndPort> startNodes;
4647

4748
private static final int MASTER_NODE_INDEX = 2;
@@ -61,19 +62,35 @@ public void run() {
6162
}
6263

6364
public JedisClusterInfoCache(final JedisClientConfig clientConfig, final Set<HostAndPort> startNodes) {
64-
this(clientConfig, null, startNodes);
65+
this(clientConfig, null, null, startNodes);
66+
}
67+
68+
public JedisClusterInfoCache(final JedisClientConfig clientConfig, ClientSideCache csCache, final Set<HostAndPort> startNodes) {
69+
this(clientConfig, csCache, null, startNodes);
6570
}
6671

6772
public JedisClusterInfoCache(final JedisClientConfig clientConfig,
6873
final GenericObjectPoolConfig<Connection> poolConfig, final Set<HostAndPort> startNodes) {
69-
this(clientConfig, poolConfig, startNodes, null);
74+
this(clientConfig, null, poolConfig, startNodes);
75+
}
76+
77+
public JedisClusterInfoCache(final JedisClientConfig clientConfig, ClientSideCache csCache,
78+
final GenericObjectPoolConfig<Connection> poolConfig, final Set<HostAndPort> startNodes) {
79+
this(clientConfig, csCache, poolConfig, startNodes, null);
7080
}
7181

7282
public JedisClusterInfoCache(final JedisClientConfig clientConfig,
7383
final GenericObjectPoolConfig<Connection> poolConfig, final Set<HostAndPort> startNodes,
7484
final Duration topologyRefreshPeriod) {
85+
this(clientConfig, null, poolConfig, startNodes, topologyRefreshPeriod);
86+
}
87+
88+
public JedisClusterInfoCache(final JedisClientConfig clientConfig, ClientSideCache csCache,
89+
final GenericObjectPoolConfig<Connection> poolConfig, final Set<HostAndPort> startNodes,
90+
final Duration topologyRefreshPeriod) {
7591
this.poolConfig = poolConfig;
7692
this.clientConfig = clientConfig;
93+
this.clientSideCache = csCache;
7794
this.startNodes = startNodes;
7895
if (topologyRefreshPeriod != null) {
7996
logger.info("Cluster topology refresh start, period: {}, startNodes: {}", topologyRefreshPeriod, startNodes);
@@ -209,6 +226,9 @@ private void discoverClusterSlots(Connection jedis) {
209226
try {
210227
Arrays.fill(slots, null);
211228
Arrays.fill(slotNodes, null);
229+
if (clientSideCache != null) {
230+
clientSideCache.clear();
231+
}
212232
Set<String> hostAndPortKeys = new HashSet<>();
213233

214234
for (Object slotInfoObj : slotsInfo) {
@@ -270,15 +290,30 @@ public ConnectionPool setupNodeIfNotExist(final HostAndPort node) {
270290
ConnectionPool existingPool = nodes.get(nodeKey);
271291
if (existingPool != null) return existingPool;
272292

273-
ConnectionPool nodePool = poolConfig == null ? new ConnectionPool(node, clientConfig)
274-
: new ConnectionPool(node, clientConfig, poolConfig);
293+
ConnectionPool nodePool = createNodePool(node);
275294
nodes.put(nodeKey, nodePool);
276295
return nodePool;
277296
} finally {
278297
w.unlock();
279298
}
280299
}
281300

301+
private ConnectionPool createNodePool(HostAndPort node) {
302+
if (poolConfig == null) {
303+
if (clientSideCache == null) {
304+
return new ConnectionPool(node, clientConfig);
305+
} else {
306+
return new ConnectionPool(node, clientConfig, clientSideCache);
307+
}
308+
} else {
309+
if (clientSideCache == null) {
310+
return new ConnectionPool(node, clientConfig, poolConfig);
311+
} else {
312+
return new ConnectionPool(node, clientConfig, clientSideCache, poolConfig);
313+
}
314+
}
315+
}
316+
282317
public void assignSlotToNode(int slot, HostAndPort targetNode) {
283318
w.lock();
284319
try {

0 commit comments

Comments
 (0)