Skip to content

Commit

Permalink
Support TTL in client side caching (using Caffeine library)
Browse files Browse the repository at this point in the history
  • Loading branch information
sazzad16 committed Jan 24, 2024
1 parent 3ab6bdc commit f66697f
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 55 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@
<artifactId>gson</artifactId>
<version>2.10.1</version>
</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>
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
50 changes: 50 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,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<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);
}
}
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
39 changes: 39 additions & 0 deletions src/test/java/redis/clients/jedis/util/MapCSC.java
Original file line number Diff line number Diff line change
@@ -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<ByteBuffer, Object> cache;

public MapCSC() {
this(new HashMap<>());
}

public MapCSC(Map<ByteBuffer, Object> 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);
}
}

0 comments on commit f66697f

Please sign in to comment.