diff --git a/pom.xml b/pom.xml index c9fd15d2cb..96a4e122f7 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,12 @@ gson 2.10.1 + + com.github.ben-manes.caffeine + caffeine + 2.9.3 + true + diff --git a/src/main/java/redis/clients/jedis/ClientSideCache.java b/src/main/java/redis/clients/jedis/ClientSideCache.java index 62c5be28c2..482c9c0681 100644 --- a/src/main/java/redis/clients/jedis/ClientSideCache.java +++ b/src/main/java/redis/clients/jedis/ClientSideCache.java @@ -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 cache; + protected ClientSideCache() { } - public ClientSideCache() { - this.cache = new HashMap<>(); - } + public abstract void clear(); - /** - * For testing purpose only. - * @param map - */ - ClientSideCache(Map 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 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 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 { diff --git a/src/main/java/redis/clients/jedis/UnifiedJedis.java b/src/main/java/redis/clients/jedis/UnifiedJedis.java index ba7b36b134..343b24c842 100644 --- a/src/main/java/redis/clients/jedis/UnifiedJedis.java +++ b/src/main/java/redis/clients/jedis/UnifiedJedis.java @@ -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)); diff --git a/src/main/java/redis/clients/jedis/util/CaffeineCSC.java b/src/main/java/redis/clients/jedis/util/CaffeineCSC.java new file mode 100644 index 0000000000..7a205f40ab --- /dev/null +++ b/src/main/java/redis/clients/jedis/util/CaffeineCSC.java @@ -0,0 +1,50 @@ +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 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 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); + } +} diff --git a/src/test/java/redis/clients/jedis/JedisClusterClientSideCacheTest.java b/src/test/java/redis/clients/jedis/JedisClusterClientSideCacheTest.java index 3c8bc18c5c..8e276d9665 100644 --- a/src/test/java/redis/clients/jedis/JedisClusterClientSideCacheTest.java +++ b/src/test/java/redis/clients/jedis/JedisClusterClientSideCacheTest.java @@ -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 { @@ -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"); @@ -42,7 +43,7 @@ public void simple() { @Test public void simpleWithSimpleMap() { HashMap 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")); @@ -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(); @@ -71,7 +72,7 @@ public void flushAll() { @Test public void flushAllWithSimpleMap() { HashMap 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")); diff --git a/src/test/java/redis/clients/jedis/JedisPooledClientSideCacheTest.java b/src/test/java/redis/clients/jedis/JedisPooledClientSideCacheTest.java index ad4313a4b7..805d9738d7 100644 --- a/src/test/java/redis/clients/jedis/JedisPooledClientSideCacheTest.java +++ b/src/test/java/redis/clients/jedis/JedisPooledClientSideCacheTest.java @@ -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 { @@ -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"); @@ -53,7 +54,7 @@ public void simple() { @Test public void simpleWithSimpleMap() { HashMap 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")); @@ -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(); @@ -82,7 +83,7 @@ public void flushAll() { @Test public void flushAllWithSimpleMap() { HashMap 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")); diff --git a/src/test/java/redis/clients/jedis/JedisSentineledClientSideCacheTest.java b/src/test/java/redis/clients/jedis/JedisSentineledClientSideCacheTest.java index 9af243ffc7..5fa5a36f4b 100644 --- a/src/test/java/redis/clients/jedis/JedisSentineledClientSideCacheTest.java +++ b/src/test/java/redis/clients/jedis/JedisSentineledClientSideCacheTest.java @@ -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 { @@ -28,16 +27,9 @@ public class JedisSentineledClientSideCacheTest { private static final JedisClientConfig sentinelClientConfig = DefaultJedisClientConfig.builder().resp3().build(); - private static final Supplier> 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"); @@ -48,7 +40,7 @@ public void simple() { @Test public void simpleWithSimpleMap() { HashMap 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")); @@ -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(); @@ -77,7 +69,7 @@ public void flushAll() { @Test public void flushAllWithSimpleMap() { HashMap 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")); diff --git a/src/test/java/redis/clients/jedis/util/MapCSC.java b/src/test/java/redis/clients/jedis/util/MapCSC.java new file mode 100644 index 0000000000..a9d1ef6f37 --- /dev/null +++ b/src/test/java/redis/clients/jedis/util/MapCSC.java @@ -0,0 +1,39 @@ +package redis.clients.jedis.util; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import redis.clients.jedis.ClientSideCache; + +public class MapCSC extends ClientSideCache { + + private final Map cache; + + public MapCSC() { + this(new HashMap<>()); + } + + public MapCSC(Map map) { + this.cache = map; + } + + @Override + public final void clear() { + cache.clear(); + } + + @Override + protected void remove(ByteBuffer key) { + cache.remove(key); + } + + @Override + protected void put(ByteBuffer key, Object value) { + cache.put(key, value); + } + + @Override + protected Object get(ByteBuffer key) { + return cache.get(key); + } +}