diff --git a/src/main/java/redis/clients/jedis/Connection.java b/src/main/java/redis/clients/jedis/Connection.java index bff5898f8d..b2d2aac27e 100644 --- a/src/main/java/redis/clients/jedis/Connection.java +++ b/src/main/java/redis/clients/jedis/Connection.java @@ -18,6 +18,7 @@ import redis.clients.jedis.args.ClientAttributeOption; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.commands.ProtocolCommand; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.exceptions.JedisException; diff --git a/src/main/java/redis/clients/jedis/ConnectionFactory.java b/src/main/java/redis/clients/jedis/ConnectionFactory.java index 5b43606205..ecfceb77ef 100644 --- a/src/main/java/redis/clients/jedis/ConnectionFactory.java +++ b/src/main/java/redis/clients/jedis/ConnectionFactory.java @@ -6,7 +6,7 @@ import org.apache.commons.pool2.impl.DefaultPooledObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.exceptions.JedisException; /** diff --git a/src/main/java/redis/clients/jedis/ConnectionPool.java b/src/main/java/redis/clients/jedis/ConnectionPool.java index 70202deeae..59f416649c 100644 --- a/src/main/java/redis/clients/jedis/ConnectionPool.java +++ b/src/main/java/redis/clients/jedis/ConnectionPool.java @@ -2,6 +2,7 @@ import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.util.Pool; public class ConnectionPool extends Pool { diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index 0138058d66..72a9495617 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -7,6 +7,7 @@ import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.providers.ClusterConnectionProvider; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.util.JedisClusterCRC16; public class JedisCluster extends UnifiedJedis { diff --git a/src/main/java/redis/clients/jedis/JedisClusterInfoCache.java b/src/main/java/redis/clients/jedis/JedisClusterInfoCache.java index 5646dbfb59..482fe12cb5 100644 --- a/src/main/java/redis/clients/jedis/JedisClusterInfoCache.java +++ b/src/main/java/redis/clients/jedis/JedisClusterInfoCache.java @@ -21,6 +21,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.exceptions.JedisClusterOperationException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.SafeEncoder; diff --git a/src/main/java/redis/clients/jedis/JedisPooled.java b/src/main/java/redis/clients/jedis/JedisPooled.java index a5840c6530..4bda1d1ebb 100644 --- a/src/main/java/redis/clients/jedis/JedisPooled.java +++ b/src/main/java/redis/clients/jedis/JedisPooled.java @@ -7,6 +7,7 @@ import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.providers.PooledConnectionProvider; import redis.clients.jedis.util.JedisURIHelper; import redis.clients.jedis.util.Pool; diff --git a/src/main/java/redis/clients/jedis/JedisSentineled.java b/src/main/java/redis/clients/jedis/JedisSentineled.java index f1cb8ea650..164c665d3f 100644 --- a/src/main/java/redis/clients/jedis/JedisSentineled.java +++ b/src/main/java/redis/clients/jedis/JedisSentineled.java @@ -2,6 +2,7 @@ import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.providers.SentineledConnectionProvider; public class JedisSentineled extends UnifiedJedis { diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index 4bd82fec1e..dc84974db7 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -11,6 +11,7 @@ import redis.clients.jedis.exceptions.*; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.commands.ProtocolCommand; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.RedisInputStream; import redis.clients.jedis.util.RedisOutputStream; diff --git a/src/main/java/redis/clients/jedis/UnifiedJedis.java b/src/main/java/redis/clients/jedis/UnifiedJedis.java index 37c4316ec0..57fea68497 100644 --- a/src/main/java/redis/clients/jedis/UnifiedJedis.java +++ b/src/main/java/redis/clients/jedis/UnifiedJedis.java @@ -17,6 +17,7 @@ import redis.clients.jedis.commands.SampleBinaryKeyedCommands; import redis.clients.jedis.commands.SampleKeyedCommands; import redis.clients.jedis.commands.RedisModuleCommands; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.executors.*; import redis.clients.jedis.gears.TFunctionListParams; @@ -300,11 +301,11 @@ public void setBroadcastAndRoundRobinConfig(JedisBroadcastAndRoundRobinConfig co } private T checkAndClientSideCacheCommand(CommandObject command, Object... keys) { - if (clientSideCache == null) { - return executeCommand(command); + if (clientSideCache != null) { + return clientSideCache.get((cmd) -> executeCommand(cmd), command, keys); } - return clientSideCache.getValue((cmd) -> executeCommand(cmd), command, keys); + return executeCommand(command); } public String ping() { diff --git a/src/main/java/redis/clients/jedis/ClientSideCache.java b/src/main/java/redis/clients/jedis/csc/ClientSideCache.java similarity index 64% rename from src/main/java/redis/clients/jedis/ClientSideCache.java rename to src/main/java/redis/clients/jedis/csc/ClientSideCache.java index 389b795814..22509e4e2c 100644 --- a/src/main/java/redis/clients/jedis/ClientSideCache.java +++ b/src/main/java/redis/clients/jedis/csc/ClientSideCache.java @@ -1,4 +1,4 @@ -package redis.clients.jedis; +package redis.clients.jedis.csc; import java.nio.ByteBuffer; import java.util.HashSet; @@ -6,41 +6,46 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; - import java.util.function.Function; +import redis.clients.jedis.CommandObject; import redis.clients.jedis.util.SafeEncoder; /** * The class to manage the client-side caching. User can provide any of implementation of this class to the client - * object; e.g. {@link redis.clients.jedis.util.CaffeineCSC CaffeineCSC} or - * {@link redis.clients.jedis.util.GuavaCSC GuavaCSC} or a custom implementation of their own. + * object; e.g. {@link redis.clients.jedis.csc.util.CaffeineCSC CaffeineCSC} or + * {@link redis.clients.jedis.csc.util.GuavaCSC GuavaCSC} or a custom implementation of their own. */ public abstract class ClientSideCache { protected static final int DEFAULT_MAXIMUM_SIZE = 10_000; protected static final int DEFAULT_EXPIRE_SECONDS = 100; - private final Map> keyToCommandHashes; + private final Map> keyToCommandHashes = new ConcurrentHashMap<>(); + private final ClientSideCacheable cacheable; protected ClientSideCache() { - this.keyToCommandHashes = new ConcurrentHashMap<>(); + this.cacheable = DefaultClientSideCacheable.INSTANCE; + } + + protected ClientSideCache(ClientSideCacheable cacheable) { + this.cacheable = cacheable; } - protected abstract void invalidateAllCommandHashes(); + protected abstract void invalidateAllHashes(); - protected abstract void invalidateCommandHashes(Iterable hashes); + protected abstract void invalidateHashes(Iterable hashes); - protected abstract void put(long hash, Object value); + protected abstract void putValue(long hash, Object value); - protected abstract Object get(long hash); + protected abstract Object getValue(long hash); - protected abstract long getCommandHash(CommandObject command); + protected abstract long getHash(CommandObject command); public final void clear() { invalidateAllKeysAndCommandHashes(); } - final void invalidate(List list) { + public final void invalidate(List list) { if (list == null) { invalidateAllKeysAndCommandHashes(); return; @@ -50,7 +55,7 @@ final void invalidate(List list) { } private void invalidateAllKeysAndCommandHashes() { - invalidateAllCommandHashes(); + invalidateAllHashes(); keyToCommandHashes.clear(); } @@ -63,23 +68,27 @@ private void invalidateKeyAndRespectiveCommandHashes(Object key) { Set hashes = keyToCommandHashes.get(mapKey); if (hashes != null) { - invalidateCommandHashes(hashes); + invalidateHashes(hashes); keyToCommandHashes.remove(mapKey); } } - final T getValue(Function, T> loader, CommandObject command, Object... keys) { + public final T get(Function, T> loader, CommandObject command, Object... keys) { + + if (!cacheable.isCacheable(command.getArguments().getCommand(), keys)) { + return loader.apply(command); + } - final long hash = getCommandHash(command); + final long hash = getHash(command); - T value = (T) get(hash); + T value = (T) getValue(hash); if (value != null) { return value; } value = loader.apply(command); if (value != null) { - put(hash, value); + putValue(hash, value); for (Object key : keys) { ByteBuffer mapKey = makeKeyForKeyToCommandHashes(key); if (keyToCommandHashes.containsKey(mapKey)) { diff --git a/src/main/java/redis/clients/jedis/csc/ClientSideCacheable.java b/src/main/java/redis/clients/jedis/csc/ClientSideCacheable.java new file mode 100644 index 0000000000..d3c782ad00 --- /dev/null +++ b/src/main/java/redis/clients/jedis/csc/ClientSideCacheable.java @@ -0,0 +1,8 @@ +package redis.clients.jedis.csc; + +import redis.clients.jedis.commands.ProtocolCommand; + +public interface ClientSideCacheable { + + boolean isCacheable(ProtocolCommand command, Object... keys); +} diff --git a/src/main/java/redis/clients/jedis/csc/DefaultClientSideCacheable.java b/src/main/java/redis/clients/jedis/csc/DefaultClientSideCacheable.java new file mode 100644 index 0000000000..d97ca6a2ee --- /dev/null +++ b/src/main/java/redis/clients/jedis/csc/DefaultClientSideCacheable.java @@ -0,0 +1,15 @@ +package redis.clients.jedis.csc; + +import redis.clients.jedis.commands.ProtocolCommand; + +public class DefaultClientSideCacheable implements ClientSideCacheable { + + public static final DefaultClientSideCacheable INSTANCE = new DefaultClientSideCacheable(); + + public DefaultClientSideCacheable() { } + + @Override + public boolean isCacheable(ProtocolCommand command, Object... keys) { + return true; + } +} diff --git a/src/main/java/redis/clients/jedis/csc/util/AllowAndDenyListWithStringKeys.java b/src/main/java/redis/clients/jedis/csc/util/AllowAndDenyListWithStringKeys.java new file mode 100644 index 0000000000..e9adbea37c --- /dev/null +++ b/src/main/java/redis/clients/jedis/csc/util/AllowAndDenyListWithStringKeys.java @@ -0,0 +1,36 @@ +package redis.clients.jedis.csc.util; + +import java.util.Set; +import redis.clients.jedis.commands.ProtocolCommand; +import redis.clients.jedis.csc.ClientSideCacheable; + +public class AllowAndDenyListWithStringKeys implements ClientSideCacheable { + + private final Set allowCommands; + private final Set denyCommands; + + private final Set allowKeys; + private final Set denyKeys; + + public AllowAndDenyListWithStringKeys(Set allowCommands, Set denyCommands, + Set allowKeys, Set denyKeys) { + this.allowCommands = allowCommands; + this.denyCommands = denyCommands; + this.allowKeys = allowKeys; + this.denyKeys = denyKeys; + } + + @Override + public boolean isCacheable(ProtocolCommand command, Object... keys) { + if (allowCommands != null && !allowCommands.contains(command)) return false; + if (denyCommands != null && denyCommands.contains(command)) return false; + + for (Object key : keys) { + if (!(key instanceof String)) return false; + if (allowKeys != null && !allowKeys.contains((String) key)) return false; + if (denyKeys != null && denyKeys.contains((String) key)) return false; + } + + return true; + } +} diff --git a/src/main/java/redis/clients/jedis/util/CaffeineCSC.java b/src/main/java/redis/clients/jedis/csc/util/CaffeineCSC.java similarity index 59% rename from src/main/java/redis/clients/jedis/util/CaffeineCSC.java rename to src/main/java/redis/clients/jedis/csc/util/CaffeineCSC.java index 3bce3504b3..4362169550 100644 --- a/src/main/java/redis/clients/jedis/util/CaffeineCSC.java +++ b/src/main/java/redis/clients/jedis/csc/util/CaffeineCSC.java @@ -1,54 +1,64 @@ -package redis.clients.jedis.util; +package redis.clients.jedis.csc.util; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import java.util.concurrent.TimeUnit; import net.openhft.hashing.LongHashFunction; -import redis.clients.jedis.ClientSideCache; + import redis.clients.jedis.CommandObject; import redis.clients.jedis.args.Rawable; +import redis.clients.jedis.csc.ClientSideCache; +import redis.clients.jedis.csc.ClientSideCacheable; +import redis.clients.jedis.csc.DefaultClientSideCacheable; public class CaffeineCSC extends ClientSideCache { private static final LongHashFunction DEFAULT_HASH_FUNCTION = LongHashFunction.xx3(); private final Cache cache; - private final LongHashFunction function; + private final LongHashFunction hashFunction; public CaffeineCSC(Cache caffeineCache, LongHashFunction hashFunction) { + super(); + this.cache = caffeineCache; + this.hashFunction = hashFunction; + } + + public CaffeineCSC(Cache caffeineCache, LongHashFunction function, ClientSideCacheable cacheable) { + super(cacheable); this.cache = caffeineCache; - this.function = hashFunction; + this.hashFunction = function; } @Override - protected final void invalidateAllCommandHashes() { + protected final void invalidateAllHashes() { cache.invalidateAll(); } @Override - protected void invalidateCommandHashes(Iterable hashes) { + protected void invalidateHashes(Iterable hashes) { cache.invalidateAll(hashes); } @Override - protected void put(long hash, Object value) { + protected void putValue(long hash, Object value) { cache.put(hash, value); } @Override - protected Object get(long hash) { + protected Object getValue(long hash) { return cache.getIfPresent(hash); } @Override - protected final long getCommandHash(CommandObject command) { + protected final long getHash(CommandObject command) { long[] nums = new long[command.getArguments().size() + 1]; int idx = 0; for (Rawable raw : command.getArguments()) { - nums[idx++] = function.hashBytes(raw.getRaw()); + nums[idx++] = hashFunction.hashBytes(raw.getRaw()); } - nums[idx] = function.hashInt(command.getBuilder().hashCode()); - return function.hashLongs(nums); + nums[idx] = hashFunction.hashInt(command.getBuilder().hashCode()); + return hashFunction.hashLongs(nums); } public static Builder builder() { @@ -63,6 +73,8 @@ public static class Builder { private LongHashFunction hashFunction = DEFAULT_HASH_FUNCTION; + private ClientSideCacheable cacheable = DefaultClientSideCacheable.INSTANCE; + private Builder() { } public Builder maximumSize(int size) { @@ -80,6 +92,11 @@ public Builder hashFunction(LongHashFunction function) { return this; } + public Builder cacheable(ClientSideCacheable cacheable) { + this.cacheable = cacheable; + return this; + } + public CaffeineCSC build() { Caffeine cb = Caffeine.newBuilder(); @@ -87,7 +104,7 @@ public CaffeineCSC build() { cb.expireAfterWrite(expireTime, expireTimeUnit); - return new CaffeineCSC(cb.build(), hashFunction); + return new CaffeineCSC(cb.build(), hashFunction, cacheable); } } } diff --git a/src/main/java/redis/clients/jedis/util/GuavaCSC.java b/src/main/java/redis/clients/jedis/csc/util/GuavaCSC.java similarity index 61% rename from src/main/java/redis/clients/jedis/util/GuavaCSC.java rename to src/main/java/redis/clients/jedis/csc/util/GuavaCSC.java index 934d5b15ab..e1c9e4f434 100644 --- a/src/main/java/redis/clients/jedis/util/GuavaCSC.java +++ b/src/main/java/redis/clients/jedis/csc/util/GuavaCSC.java @@ -1,52 +1,66 @@ -package redis.clients.jedis.util; +package redis.clients.jedis.csc.util; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.hash.HashFunction; import com.google.common.hash.Hasher; import java.util.concurrent.TimeUnit; -import redis.clients.jedis.ClientSideCache; + import redis.clients.jedis.CommandObject; +import redis.clients.jedis.csc.ClientSideCache; +import redis.clients.jedis.csc.ClientSideCacheable; +import redis.clients.jedis.csc.DefaultClientSideCacheable; public class GuavaCSC extends ClientSideCache { private static final HashFunction DEFAULT_HASH_FUNCTION = com.google.common.hash.Hashing.fingerprint2011(); private final Cache cache; - private final HashFunction function; + private final HashFunction hashFunction; public GuavaCSC(Cache guavaCache) { this(guavaCache, DEFAULT_HASH_FUNCTION); } public GuavaCSC(Cache guavaCache, HashFunction hashFunction) { + super(); this.cache = guavaCache; - this.function = hashFunction; + this.hashFunction = hashFunction; + } + + public GuavaCSC(Cache guavaCache, ClientSideCacheable cacheable) { + this(guavaCache, DEFAULT_HASH_FUNCTION, cacheable); + } + + public GuavaCSC(Cache cache, HashFunction function, ClientSideCacheable cacheable) { + super(cacheable); + this.cache = cache; + this.hashFunction = function; } @Override - protected final void invalidateAllCommandHashes() { + protected final void invalidateAllHashes() { cache.invalidateAll(); } @Override - protected void invalidateCommandHashes(Iterable hashes) { + protected void invalidateHashes(Iterable hashes) { cache.invalidateAll(hashes); } @Override - protected void put(long hash, Object value) { + protected void putValue(long hash, Object value) { cache.put(hash, value); } @Override - protected Object get(long hash) { + protected Object getValue(long hash) { return cache.getIfPresent(hash); } @Override - protected final long getCommandHash(CommandObject command) { - Hasher hasher = function.newHasher(); + protected final long getHash(CommandObject command) { + Hasher hasher = hashFunction.newHasher(); command.getArguments().forEach(raw -> hasher.putBytes(raw.getRaw())); hasher.putInt(command.getBuilder().hashCode()); return hasher.hash().asLong(); @@ -64,6 +78,8 @@ public static class Builder { private HashFunction hashFunction = DEFAULT_HASH_FUNCTION; + private ClientSideCacheable cacheable = DefaultClientSideCacheable.INSTANCE; + private Builder() { } public Builder maximumSize(int size) { @@ -81,6 +97,11 @@ public Builder hashFunction(HashFunction function) { return this; } + public Builder cacheable(ClientSideCacheable cacheable) { + this.cacheable = cacheable; + return this; + } + public GuavaCSC build() { CacheBuilder cb = CacheBuilder.newBuilder(); @@ -88,7 +109,7 @@ public GuavaCSC build() { cb.expireAfterWrite(expireTime, expireTimeUnit); - return new GuavaCSC(cb.build(), hashFunction); + return new GuavaCSC(cb.build(), hashFunction, cacheable); } } } diff --git a/src/main/java/redis/clients/jedis/providers/ClusterConnectionProvider.java b/src/main/java/redis/clients/jedis/providers/ClusterConnectionProvider.java index be83aa7388..6b148a5e77 100644 --- a/src/main/java/redis/clients/jedis/providers/ClusterConnectionProvider.java +++ b/src/main/java/redis/clients/jedis/providers/ClusterConnectionProvider.java @@ -8,7 +8,6 @@ import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; -import redis.clients.jedis.ClientSideCache; import redis.clients.jedis.ClusterCommandArguments; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.HostAndPort; @@ -16,6 +15,7 @@ import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.JedisClusterInfoCache; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.exceptions.JedisClusterOperationException; import redis.clients.jedis.exceptions.JedisException; diff --git a/src/main/java/redis/clients/jedis/providers/PooledConnectionProvider.java b/src/main/java/redis/clients/jedis/providers/PooledConnectionProvider.java index 85fa3cecd2..0d85fabad2 100644 --- a/src/main/java/redis/clients/jedis/providers/PooledConnectionProvider.java +++ b/src/main/java/redis/clients/jedis/providers/PooledConnectionProvider.java @@ -5,13 +5,13 @@ import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; -import redis.clients.jedis.ClientSideCache; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionFactory; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.util.Pool; public class PooledConnectionProvider implements ConnectionProvider { diff --git a/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java b/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java index e335803b62..1d7c5790c2 100644 --- a/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java +++ b/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java @@ -10,7 +10,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import redis.clients.jedis.ClientSideCache; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPool; @@ -18,6 +17,7 @@ import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.JedisPubSub; +import redis.clients.jedis.csc.ClientSideCache; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.IOUtils; diff --git a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java index 2d73cf04d4..e8e62ae9b5 100644 --- a/src/main/java/redis/clients/jedis/util/JedisURIHelper.java +++ b/src/main/java/redis/clients/jedis/util/JedisURIHelper.java @@ -1,11 +1,15 @@ package redis.clients.jedis.util; import java.net.URI; -import redis.clients.jedis.ClientSideCache; + import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; +import redis.clients.jedis.csc.ClientSideCache; +import redis.clients.jedis.csc.util.GuavaCSC; +import redis.clients.jedis.csc.util.CaffeineCSC; + public final class JedisURIHelper { private static final String REDIS = "redis"; diff --git a/src/test/java/redis/clients/jedis/csc/AllowAndDenyListClientSideCacheTest.java b/src/test/java/redis/clients/jedis/csc/AllowAndDenyListClientSideCacheTest.java new file mode 100644 index 0000000000..86c6dbc42f --- /dev/null +++ b/src/test/java/redis/clients/jedis/csc/AllowAndDenyListClientSideCacheTest.java @@ -0,0 +1,117 @@ +package redis.clients.jedis.csc; + +import static java.util.Collections.singleton; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.function.Supplier; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import redis.clients.jedis.Connection; +import redis.clients.jedis.ConnectionPoolConfig; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.HostAndPorts; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Protocol; +import redis.clients.jedis.csc.util.AllowAndDenyListWithStringKeys; + +public class AllowAndDenyListClientSideCacheTest { + + protected static final HostAndPort hnp = HostAndPorts.getRedisServers().get(1); + + protected Jedis control; + + @Before + public void setUp() throws Exception { + control = new Jedis(hnp, DefaultJedisClientConfig.builder().password("foobared").build()); + control.flushAll(); + } + + @After + public void tearDown() throws Exception { + control.close(); + } + + private static final Supplier clientConfig + = () -> DefaultJedisClientConfig.builder().resp3().password("foobared").build(); + + private static final Supplier> singleConnectionPoolConfig + = () -> { + ConnectionPoolConfig poolConfig = new ConnectionPoolConfig(); + poolConfig.setMaxTotal(1); + return poolConfig; + }; + + @Test + public void none() { + HashMap map = new HashMap<>(); + try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), + new MapCSC(map, new AllowAndDenyListWithStringKeys(null, null, null, null)), + singleConnectionPoolConfig.get())) { + control.set("foo", "bar"); + assertThat(map, Matchers.aMapWithSize(0)); + assertEquals("bar", jedis.get("foo")); + assertThat(map, Matchers.aMapWithSize(1)); + } + } + + @Test + public void whiteListCommand() { + HashMap map = new HashMap<>(); + try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), + new MapCSC(map, new AllowAndDenyListWithStringKeys(singleton(Protocol.Command.GET), null, null, null)), + singleConnectionPoolConfig.get())) { + control.set("foo", "bar"); + assertThat(map, Matchers.aMapWithSize(0)); + assertEquals("bar", jedis.get("foo")); + assertThat(map, Matchers.aMapWithSize(1)); + } + } + + @Test + public void blackListCommand() { + HashMap map = new HashMap<>(); + try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), + new MapCSC(map, new AllowAndDenyListWithStringKeys(null, singleton(Protocol.Command.GET), null, null)), + singleConnectionPoolConfig.get())) { + control.set("foo", "bar"); + assertThat(map, Matchers.aMapWithSize(0)); + assertEquals("bar", jedis.get("foo")); + assertThat(map, Matchers.aMapWithSize(0)); + } + } + + @Test + public void whiteListKey() { + HashMap map = new HashMap<>(); + try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), + new MapCSC(map, new AllowAndDenyListWithStringKeys(null, null, singleton("foo"), null)), + singleConnectionPoolConfig.get())) { + control.set("foo", "bar"); + assertThat(map, Matchers.aMapWithSize(0)); + assertEquals("bar", jedis.get("foo")); + assertThat(map, Matchers.aMapWithSize(1)); + } + } + + @Test + public void blackListKey() { + HashMap map = new HashMap<>(); + try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), + new MapCSC(map, new AllowAndDenyListWithStringKeys(null, null, null, singleton("foo"))), + singleConnectionPoolConfig.get())) { + control.set("foo", "bar"); + assertThat(map, Matchers.aMapWithSize(0)); + assertEquals("bar", jedis.get("foo")); + assertThat(map, Matchers.aMapWithSize(0)); + } + } +} diff --git a/src/test/java/redis/clients/jedis/ClientSideCacheLibsTest.java b/src/test/java/redis/clients/jedis/csc/ClientSideCacheLibsTest.java similarity index 83% rename from src/test/java/redis/clients/jedis/ClientSideCacheLibsTest.java rename to src/test/java/redis/clients/jedis/csc/ClientSideCacheLibsTest.java index f6469c09b0..31589458f8 100644 --- a/src/test/java/redis/clients/jedis/ClientSideCacheLibsTest.java +++ b/src/test/java/redis/clients/jedis/csc/ClientSideCacheLibsTest.java @@ -1,4 +1,4 @@ -package redis.clients.jedis; +package redis.clients.jedis.csc; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; @@ -6,18 +6,25 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.cache.CacheBuilder; - import java.util.function.Supplier; import net.openhft.hashing.LongHashFunction; -import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; -import redis.clients.jedis.util.CaffeineCSC; -import redis.clients.jedis.util.GuavaCSC; +import redis.clients.jedis.Connection; +import redis.clients.jedis.ConnectionPoolConfig; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.HostAndPorts; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.csc.util.CaffeineCSC; +import redis.clients.jedis.csc.util.GuavaCSC; public class ClientSideCacheLibsTest { @@ -48,7 +55,8 @@ public void tearDown() throws Exception { @Test public void guavaSimple() { - GuavaCSC guava = GuavaCSC.builder().maximumSize(10).ttl(10).hashFunction(com.google.common.hash.Hashing.farmHashFingerprint64()).build(); + GuavaCSC guava = GuavaCSC.builder().maximumSize(10).ttl(10) + .hashFunction(com.google.common.hash.Hashing.farmHashFingerprint64()).build(); try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), guava)) { control.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); @@ -62,7 +70,8 @@ public void guavaMore() { com.google.common.cache.Cache guava = CacheBuilder.newBuilder().recordStats().build(); - try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new GuavaCSC(guava), singleConnectionPoolConfig.get())) { + try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new GuavaCSC(guava), + singleConnectionPoolConfig.get())) { control.set("foo", "bar"); assertEquals(0, guava.size()); assertEquals("bar", jedis.get("foo")); @@ -98,8 +107,8 @@ public void caffeineMore() { com.github.benmanes.caffeine.cache.Cache caffeine = Caffeine.newBuilder().recordStats().build(); - try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new CaffeineCSC(caffeine, LongHashFunction.city_1_1()), - singleConnectionPoolConfig.get())) { + try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), + new CaffeineCSC(caffeine, LongHashFunction.city_1_1()), singleConnectionPoolConfig.get())) { control.set("foo", "bar"); assertEquals(0, caffeine.estimatedSize()); assertEquals("bar", jedis.get("foo")); diff --git a/src/test/java/redis/clients/jedis/JedisClusterClientSideCacheTest.java b/src/test/java/redis/clients/jedis/csc/JedisClusterClientSideCacheTest.java similarity index 85% rename from src/test/java/redis/clients/jedis/JedisClusterClientSideCacheTest.java rename to src/test/java/redis/clients/jedis/csc/JedisClusterClientSideCacheTest.java index 60ddf002d7..3b52072560 100644 --- a/src/test/java/redis/clients/jedis/JedisClusterClientSideCacheTest.java +++ b/src/test/java/redis/clients/jedis/csc/JedisClusterClientSideCacheTest.java @@ -1,4 +1,4 @@ -package redis.clients.jedis; +package redis.clients.jedis.csc; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; @@ -13,7 +13,14 @@ import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.hamcrest.Matchers; import org.junit.Test; -import redis.clients.jedis.util.MapCSC; + +import redis.clients.jedis.Connection; +import redis.clients.jedis.ConnectionPoolConfig; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.JedisCluster; +import redis.clients.jedis.JedisClusterTestBase; public class JedisClusterClientSideCacheTest extends JedisClusterTestBase { @@ -42,7 +49,8 @@ public void simple() { @Test public void simpleWithSimpleMap() { HashMap map = new HashMap<>(); - try (JedisCluster jedis = new JedisCluster(hnp, clientConfig.get(), new MapCSC(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")); @@ -71,7 +79,8 @@ public void flushAll() { @Test public void flushAllWithSimpleMap() { HashMap map = new HashMap<>(); - try (JedisCluster jedis = new JedisCluster(hnp, clientConfig.get(), new MapCSC(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/csc/JedisPooledClientSideCacheTest.java similarity index 85% rename from src/test/java/redis/clients/jedis/JedisPooledClientSideCacheTest.java rename to src/test/java/redis/clients/jedis/csc/JedisPooledClientSideCacheTest.java index 2e641e0f3a..149b1730a0 100644 --- a/src/test/java/redis/clients/jedis/JedisPooledClientSideCacheTest.java +++ b/src/test/java/redis/clients/jedis/csc/JedisPooledClientSideCacheTest.java @@ -1,4 +1,4 @@ -package redis.clients.jedis; +package redis.clients.jedis.csc; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; @@ -11,7 +11,15 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import redis.clients.jedis.util.MapCSC; + +import redis.clients.jedis.Connection; +import redis.clients.jedis.ConnectionPoolConfig; +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.HostAndPorts; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.JedisPooled; public class JedisPooledClientSideCacheTest { @@ -53,7 +61,8 @@ public void simple() { @Test public void simpleWithSimpleMap() { HashMap map = new HashMap<>(); - try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new MapCSC(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")); @@ -82,7 +91,8 @@ public void flushAll() { @Test public void flushAllWithSimpleMap() { HashMap map = new HashMap<>(); - try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new MapCSC(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/csc/JedisSentineledClientSideCacheTest.java similarity index 83% rename from src/test/java/redis/clients/jedis/JedisSentineledClientSideCacheTest.java rename to src/test/java/redis/clients/jedis/csc/JedisSentineledClientSideCacheTest.java index 9e5f720933..6390ab3194 100644 --- a/src/test/java/redis/clients/jedis/JedisSentineledClientSideCacheTest.java +++ b/src/test/java/redis/clients/jedis/csc/JedisSentineledClientSideCacheTest.java @@ -1,4 +1,4 @@ -package redis.clients.jedis; +package redis.clients.jedis.csc; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; @@ -8,10 +8,14 @@ import java.util.HashMap; import java.util.Set; import java.util.stream.Collectors; - import org.hamcrest.Matchers; import org.junit.Test; -import redis.clients.jedis.util.MapCSC; + +import redis.clients.jedis.DefaultJedisClientConfig; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.HostAndPorts; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.JedisSentineled; public class JedisSentineledClientSideCacheTest { @@ -28,7 +32,8 @@ public class JedisSentineledClientSideCacheTest { @Test public void simple() { - try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new MapCSC(), 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"); @@ -39,7 +44,8 @@ public void simple() { @Test public void simpleWithSimpleMap() { HashMap map = new HashMap<>(); - try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new MapCSC(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")); @@ -57,7 +63,8 @@ public void simpleWithSimpleMap() { @Test public void flushAll() { - try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new MapCSC(), 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(); @@ -68,7 +75,8 @@ public void flushAll() { @Test public void flushAllWithSimpleMap() { HashMap map = new HashMap<>(); - try (JedisSentineled jedis = new JedisSentineled(MASTER_NAME, masterClientConfig, new MapCSC(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/csc/MapCSC.java similarity index 60% rename from src/test/java/redis/clients/jedis/util/MapCSC.java rename to src/test/java/redis/clients/jedis/csc/MapCSC.java index eb229036ea..ee08efb588 100644 --- a/src/test/java/redis/clients/jedis/util/MapCSC.java +++ b/src/test/java/redis/clients/jedis/csc/MapCSC.java @@ -1,9 +1,9 @@ -package redis.clients.jedis.util; +package redis.clients.jedis.csc; import java.util.Arrays; +import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import redis.clients.jedis.ClientSideCache; + import redis.clients.jedis.CommandObject; import redis.clients.jedis.args.Rawable; @@ -12,35 +12,40 @@ public class MapCSC extends ClientSideCache { private final Map cache; public MapCSC() { - this(new ConcurrentHashMap<>()); + this(new HashMap<>()); } public MapCSC(Map map) { this.cache = map; } + public MapCSC(Map cache, ClientSideCacheable cacheable) { + super(cacheable); + this.cache = cache; + } + @Override - protected final void invalidateAllCommandHashes() { + protected final void invalidateAllHashes() { cache.clear(); } @Override - protected void invalidateCommandHashes(Iterable hashes) { + protected void invalidateHashes(Iterable hashes) { hashes.forEach(hash -> cache.remove(hash)); } @Override - protected void put(long hash, Object value) { + protected void putValue(long hash, Object value) { cache.put(hash, value); } @Override - protected Object get(long hash) { + protected Object getValue(long hash) { return cache.get(hash); } @Override - protected final long getCommandHash(CommandObject command) { + protected final long getHash(CommandObject command) { long result = 1; for (Rawable raw : command.getArguments()) { result = 31 * result + Arrays.hashCode(raw.getRaw());