Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support TTL in client side caching #3695

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@
<version>2.10.1</version>
</dependency>

<!-- Optional dependencies -->
<!-- Client-side caching -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.0.0-jre</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.3</version>
<optional>true</optional>
</dependency>

<!-- UNIX socket connection support -->
<dependency>
<groupId>com.kohlschutter.junixsocket</groupId>
Expand All @@ -90,6 +105,7 @@
<version>1.19.0</version>
<scope>test</scope>
</dependency>

<!-- test -->
<dependency>
<groupId>junit</groupId>
Expand Down
48 changes: 16 additions & 32 deletions src/main/java/redis/clients/jedis/ClientSideCache.java
Original file line number Diff line number Diff line change
@@ -1,63 +1,47 @@
package redis.clients.jedis;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.util.SafeEncoder;

public class ClientSideCache {
public abstract class ClientSideCache {

private final Map<ByteBuffer, Object> cache;
protected ClientSideCache() { }

public ClientSideCache() {
this.cache = new HashMap<>();
}
public abstract void clear();

/**
* For testing purpose only.
* @param map
*/
ClientSideCache(Map<ByteBuffer, Object> map) {
this.cache = map;
}
protected abstract void remove(ByteBuffer key);

public final void clear() {
cache.clear();
}
protected abstract void put(ByteBuffer key, Object value);

protected abstract Object get(ByteBuffer key);

public final void invalidateKeys(List list) {
final void invalidateKeys(List list) {
if (list == null) {
clear();
return;
} else {
list.forEach(this::invalidateKey);
}

list.forEach(this::invalidateKey);
}

private void invalidateKey(Object key) {
if (key instanceof byte[]) {
cache.remove(convertKey((byte[]) key));
remove(convertKey((byte[]) key));
} else {
throw new JedisException("" + key.getClass().getSimpleName() + " is not supported. Value: " + String.valueOf(key));
}
}

protected void setKey(Object key, Object value) {
cache.put(getMapKey(key), value);
}

protected <T> T getValue(Object key) {
return (T) getMapValue(key);
final void set(Object key, Object value) {
put(makeKey(key), value);
}

private Object getMapValue(Object key) {
return cache.get(getMapKey(key));
final <T> T get(Object key) {
return (T) get(makeKey(key));
}

private ByteBuffer getMapKey(Object key) {
private ByteBuffer makeKey(Object key) {
if (key instanceof byte[]) {
return convertKey((byte[]) key);
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/redis/clients/jedis/UnifiedJedis.java
Original file line number Diff line number Diff line change
Expand Up @@ -750,11 +750,11 @@ public String set(String key, String value, SetParams params) {
@Override
public String get(String key) {
if (clientSideCache != null) {
String cachedValue = clientSideCache.getValue(key);
String cachedValue = clientSideCache.get(key);
if (cachedValue != null) return cachedValue;

String value = executeCommand(commandObjects.get(key));
if (value != null) clientSideCache.setKey(key, value);
if (value != null) clientSideCache.set(key, value);
return value;
}
return executeCommand(commandObjects.get(key));
Expand Down
51 changes: 51 additions & 0 deletions src/main/java/redis/clients/jedis/util/CaffeineCSC.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package redis.clients.jedis.util;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
import redis.clients.jedis.ClientSideCache;

public class CaffeineCSC extends ClientSideCache {

private static final int DEFAULT_MAXIMUM_SIZE = 10_000;

private final Cache<ByteBuffer, Object> cache;

public CaffeineCSC() {
this(DEFAULT_MAXIMUM_SIZE);
}

public CaffeineCSC(int maximumSize) {
this(Caffeine.newBuilder().maximumSize(maximumSize).build());
}

public CaffeineCSC(int maximumSize, int ttlSeconds) {
this(Caffeine.newBuilder().maximumSize(maximumSize)
.expireAfterWrite(ttlSeconds, TimeUnit.SECONDS).build());
}

public CaffeineCSC(Cache<ByteBuffer, Object> caffeineCache) {
this.cache = caffeineCache;
}

@Override
public final void clear() {
cache.invalidateAll();
}

@Override
protected void remove(ByteBuffer key) {
cache.invalidate(key);
}

@Override
protected void put(ByteBuffer key, Object value) {
cache.put(key, value);
}

@Override
protected Object get(ByteBuffer key) {
return cache.getIfPresent(key);
}
}
51 changes: 51 additions & 0 deletions src/main/java/redis/clients/jedis/util/GuavaCSC.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package redis.clients.jedis.util;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
import redis.clients.jedis.ClientSideCache;

public class GuavaCSC extends ClientSideCache {

private static final int DEFAULT_MAXIMUM_SIZE = 10_000;

private final Cache<ByteBuffer, Object> cache;

public GuavaCSC() {
this(DEFAULT_MAXIMUM_SIZE);
}

public GuavaCSC(int maximumSize) {
this(CacheBuilder.newBuilder().maximumSize(maximumSize).build());
}

public GuavaCSC(int maximumSize, int ttlSeconds) {
this(CacheBuilder.newBuilder().maximumSize(maximumSize)
.expireAfterWrite(ttlSeconds, TimeUnit.SECONDS).build());
}

public GuavaCSC(Cache<ByteBuffer, Object> caffeineCache) {
this.cache = caffeineCache;
}

@Override
public final void clear() {
cache.invalidateAll();
}

@Override
protected void remove(ByteBuffer key) {
cache.invalidate(key);
}

@Override
protected void put(ByteBuffer key, Object value) {
cache.put(key, value);
}

@Override
protected Object get(ByteBuffer key) {
return cache.getIfPresent(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.hamcrest.Matchers;
import org.junit.Test;
import redis.clients.jedis.util.MapCSC;

public class JedisClusterClientSideCacheTest extends JedisClusterTestBase {

Expand All @@ -31,7 +32,7 @@ public class JedisClusterClientSideCacheTest extends JedisClusterTestBase {

@Test
public void simple() {
try (JedisCluster jedis = new JedisCluster(hnp, clientConfig.get(), new ClientSideCache())) {
try (JedisCluster jedis = new JedisCluster(hnp, clientConfig.get(), new MapCSC())) {
jedis.set("foo", "bar");
assertEquals("bar", jedis.get("foo"));
jedis.del("foo");
Expand All @@ -42,7 +43,7 @@ public void simple() {
@Test
public void simpleWithSimpleMap() {
HashMap<ByteBuffer, Object> map = new HashMap<>();
try (JedisCluster jedis = new JedisCluster(hnp, clientConfig.get(), new ClientSideCache(map), singleConnectionPoolConfig.get())) {
try (JedisCluster jedis = new JedisCluster(hnp, clientConfig.get(), new MapCSC(map), singleConnectionPoolConfig.get())) {
jedis.set("foo", "bar");
assertThat(map, Matchers.aMapWithSize(0));
assertEquals("bar", jedis.get("foo"));
Expand All @@ -60,7 +61,7 @@ public void simpleWithSimpleMap() {

@Test
public void flushAll() {
try (JedisCluster jedis = new JedisCluster(hnp, clientConfig.get(), new ClientSideCache())) {
try (JedisCluster jedis = new JedisCluster(hnp, clientConfig.get(), new MapCSC())) {
jedis.set("foo", "bar");
assertEquals("bar", jedis.get("foo"));
jedis.flushAll();
Expand All @@ -71,7 +72,7 @@ public void flushAll() {
@Test
public void flushAllWithSimpleMap() {
HashMap<ByteBuffer, Object> map = new HashMap<>();
try (JedisCluster jedis = new JedisCluster(hnp, clientConfig.get(), new ClientSideCache(map), singleConnectionPoolConfig.get())) {
try (JedisCluster jedis = new JedisCluster(hnp, clientConfig.get(), new MapCSC(map), singleConnectionPoolConfig.get())) {
jedis.set("foo", "bar");
assertThat(map, Matchers.aMapWithSize(0));
assertEquals("bar", jedis.get("foo"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.util.MapCSC;

public class JedisPooledClientSideCacheTest {

Expand Down Expand Up @@ -42,7 +43,7 @@ public void tearDown() throws Exception {

@Test
public void simple() {
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new ClientSideCache())) {
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new MapCSC())) {
control.set("foo", "bar");
assertEquals("bar", jedis.get("foo"));
control.del("foo");
Expand All @@ -53,7 +54,7 @@ public void simple() {
@Test
public void simpleWithSimpleMap() {
HashMap<ByteBuffer, Object> map = new HashMap<>();
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new ClientSideCache(map), singleConnectionPoolConfig.get())) {
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new MapCSC(map), singleConnectionPoolConfig.get())) {
control.set("foo", "bar");
assertThat(map, Matchers.aMapWithSize(0));
assertEquals("bar", jedis.get("foo"));
Expand All @@ -71,7 +72,7 @@ public void simpleWithSimpleMap() {

@Test
public void flushAll() {
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new ClientSideCache())) {
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new MapCSC())) {
control.set("foo", "bar");
assertEquals("bar", jedis.get("foo"));
control.flushAll();
Expand All @@ -82,7 +83,7 @@ public void flushAll() {
@Test
public void flushAllWithSimpleMap() {
HashMap<ByteBuffer, Object> map = new HashMap<>();
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new ClientSideCache(map), singleConnectionPoolConfig.get())) {
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new MapCSC(map), singleConnectionPoolConfig.get())) {
control.set("foo", "bar");
assertThat(map, Matchers.aMapWithSize(0));
assertEquals("bar", jedis.get("foo"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.hamcrest.Matchers;
import org.junit.Test;
import redis.clients.jedis.util.MapCSC;

public class JedisSentineledClientSideCacheTest {

Expand All @@ -28,16 +27,9 @@ public class JedisSentineledClientSideCacheTest {

private static final JedisClientConfig sentinelClientConfig = DefaultJedisClientConfig.builder().resp3().build();

private static final Supplier<GenericObjectPoolConfig<Connection>> singleConnectionPoolConfig
= () -> {
ConnectionPoolConfig poolConfig = new ConnectionPoolConfig();
poolConfig.setMaxTotal(1);
return poolConfig;
};

@Test
public void simple() {
try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new ClientSideCache(), sentinels, sentinelClientConfig)) {
try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new MapCSC(), sentinels, sentinelClientConfig)) {
jedis.set("foo", "bar");
assertEquals("bar", jedis.get("foo"));
jedis.del("foo");
Expand All @@ -48,7 +40,7 @@ public void simple() {
@Test
public void simpleWithSimpleMap() {
HashMap<ByteBuffer, Object> map = new HashMap<>();
try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new ClientSideCache(map), sentinels, sentinelClientConfig)) {
try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new MapCSC(map), sentinels, sentinelClientConfig)) {
jedis.set("foo", "bar");
assertThat(map, Matchers.aMapWithSize(0));
assertEquals("bar", jedis.get("foo"));
Expand All @@ -66,7 +58,7 @@ public void simpleWithSimpleMap() {

@Test
public void flushAll() {
try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new ClientSideCache(), sentinels, sentinelClientConfig)) {
try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new MapCSC(), sentinels, sentinelClientConfig)) {
jedis.set("foo", "bar");
assertEquals("bar", jedis.get("foo"));
jedis.flushAll();
Expand All @@ -77,7 +69,7 @@ public void flushAll() {
@Test
public void flushAllWithSimpleMap() {
HashMap<ByteBuffer, Object> map = new HashMap<>();
try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new ClientSideCache(map), sentinels, sentinelClientConfig)) {
try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new MapCSC(map), sentinels, sentinelClientConfig)) {
jedis.set("foo", "bar");
assertThat(map, Matchers.aMapWithSize(0));
assertEquals("bar", jedis.get("foo"));
Expand Down
Loading
Loading