Skip to content

Commit 82c0226

Browse files
authored
Test different functionalities of client side cache (#3828)
1 parent bb99c16 commit 82c0226

6 files changed

+339
-61
lines changed

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

+12-6
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ public final void clear() {
4949
invalidateAllKeysAndCommandHashes();
5050
}
5151

52+
public final void removeKey(Object key) {
53+
invalidateKeyAndRespectiveCommandHashes(key);
54+
}
55+
5256
public final void invalidate(List list) {
5357
if (list == null) {
5458
invalidateAllKeysAndCommandHashes();
@@ -64,11 +68,13 @@ private void invalidateAllKeysAndCommandHashes() {
6468
}
6569

6670
private void invalidateKeyAndRespectiveCommandHashes(Object key) {
67-
if (!(key instanceof byte[])) {
68-
throw new AssertionError("" + key.getClass().getSimpleName() + " is not supported. Value: " + String.valueOf(key));
69-
}
70-
71-
final ByteBuffer mapKey = makeKeyForKeyToCommandHashes((byte[]) key);
71+
// if (!(key instanceof byte[])) {
72+
// // This should be called internally. That's why throwing AssertionError instead of IllegalArgumentException.
73+
// throw new AssertionError("" + key.getClass().getSimpleName() + " is not supported. Value: " + String.valueOf(key));
74+
// }
75+
//
76+
// final ByteBuffer mapKey = makeKeyForKeyToCommandHashes((byte[]) key);
77+
final ByteBuffer mapKey = makeKeyForKeyToCommandHashes(key);
7278

7379
Set<Long> hashes = keyToCommandHashes.get(mapKey);
7480
if (hashes != null) {
@@ -111,7 +117,7 @@ public final <T> T get(Function<CommandObject<T>, T> loader, CommandObject<T> co
111117
private ByteBuffer makeKeyForKeyToCommandHashes(Object key) {
112118
if (key instanceof byte[]) return makeKeyForKeyToCommandHashes((byte[]) key);
113119
else if (key instanceof String) return makeKeyForKeyToCommandHashes(SafeEncoder.encode((String) key));
114-
else throw new AssertionError("" + key.getClass().getSimpleName() + " is not supported. Value: " + String.valueOf(key));
120+
else throw new IllegalArgumentException("" + key.getClass().getSimpleName() + " is not supported. Value: " + String.valueOf(key));
115121
}
116122

117123
private static ByteBuffer makeKeyForKeyToCommandHashes(byte[] b) {

Diff for: src/test/java/redis/clients/jedis/csc/ClientSideCacheLibsTest.java renamed to src/test/java/redis/clients/jedis/csc/CaffeineClientSideCacheTest.java

+62-55
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import static org.junit.Assert.assertEquals;
55
import static org.junit.Assert.assertNull;
66

7+
import com.github.benmanes.caffeine.cache.Cache;
78
import com.github.benmanes.caffeine.cache.Caffeine;
8-
import com.google.common.cache.CacheBuilder;
9+
import com.github.benmanes.caffeine.cache.stats.CacheStats;
10+
11+
import java.util.concurrent.TimeUnit;
912
import java.util.function.Supplier;
1013
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
1114
import org.hamcrest.Matchers;
@@ -22,73 +25,35 @@
2225
import redis.clients.jedis.JedisClientConfig;
2326
import redis.clients.jedis.JedisPooled;
2427

25-
public class ClientSideCacheLibsTest {
26-
28+
public class CaffeineClientSideCacheTest {
29+
2730
protected static final HostAndPort hnp = HostAndPorts.getRedisServers().get(1);
28-
31+
2932
protected Jedis control;
30-
33+
3134
@Before
3235
public void setUp() throws Exception {
3336
control = new Jedis(hnp, DefaultJedisClientConfig.builder().password("foobared").build());
3437
control.flushAll();
3538
}
36-
39+
3740
@After
3841
public void tearDown() throws Exception {
3942
control.close();
4043
}
41-
44+
4245
private static final Supplier<JedisClientConfig> clientConfig
4346
= () -> DefaultJedisClientConfig.builder().resp3().password("foobared").build();
44-
47+
4548
private static final Supplier<GenericObjectPoolConfig<Connection>> singleConnectionPoolConfig
4649
= () -> {
4750
ConnectionPoolConfig poolConfig = new ConnectionPoolConfig();
4851
poolConfig.setMaxTotal(1);
4952
return poolConfig;
5053
};
51-
52-
@Test
53-
public void guavaSimple() {
54-
GuavaClientSideCache guava = GuavaClientSideCache.builder().maximumSize(10).ttl(10)
55-
.hashFunction(com.google.common.hash.Hashing.farmHashFingerprint64()).build();
56-
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), guava)) {
57-
control.set("foo", "bar");
58-
assertEquals("bar", jedis.get("foo"));
59-
control.del("foo");
60-
assertThat(jedis.get("foo"), Matchers.oneOf("bar", null)); // ?
61-
}
62-
}
63-
64-
@Test
65-
public void guavaMore() {
66-
67-
com.google.common.cache.Cache guava = CacheBuilder.newBuilder().recordStats().build();
68-
69-
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new GuavaClientSideCache(guava),
70-
singleConnectionPoolConfig.get())) {
71-
control.set("foo", "bar");
72-
assertEquals(0, guava.size());
73-
assertEquals("bar", jedis.get("foo"));
74-
assertEquals(1, guava.size());
75-
control.flushAll();
76-
assertEquals(1, guava.size());
77-
assertEquals("bar", jedis.get("foo"));
78-
assertEquals(1, guava.size());
79-
jedis.ping();
80-
assertEquals(0, guava.size());
81-
assertNull(jedis.get("foo"));
82-
assertEquals(0, guava.size());
83-
}
84-
85-
com.google.common.cache.CacheStats stats = guava.stats();
86-
assertEquals(1L, stats.hitCount());
87-
assertThat(stats.missCount(), Matchers.greaterThan(0L));
88-
}
89-
54+
9055
@Test
91-
public void caffeineSimple() {
56+
public void simple() {
9257
CaffeineClientSideCache caffeine = CaffeineClientSideCache.builder().maximumSize(10).ttl(10).build();
9358
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), caffeine)) {
9459
control.set("foo", "bar");
@@ -97,12 +62,12 @@ public void caffeineSimple() {
9762
assertThat(jedis.get("foo"), Matchers.oneOf("bar", null)); // ?
9863
}
9964
}
100-
65+
10166
@Test
102-
public void caffeineMore() {
103-
104-
com.github.benmanes.caffeine.cache.Cache caffeine = Caffeine.newBuilder().recordStats().build();
105-
67+
public void individualCommandsAndThenStats() {
68+
69+
Cache caffeine = Caffeine.newBuilder().recordStats().build();
70+
10671
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(),
10772
new CaffeineClientSideCache(caffeine, new OpenHftCommandHasher()),
10873
singleConnectionPoolConfig.get())) {
@@ -119,9 +84,51 @@ public void caffeineMore() {
11984
assertNull(jedis.get("foo"));
12085
assertEquals(0, caffeine.estimatedSize());
12186
}
122-
123-
com.github.benmanes.caffeine.cache.stats.CacheStats stats = caffeine.stats();
87+
88+
CacheStats stats = caffeine.stats();
12489
assertEquals(1L, stats.hitCount());
12590
assertThat(stats.missCount(), Matchers.greaterThan(0L));
12691
}
92+
93+
@Test
94+
public void maximumSize() {
95+
final long maxSize = 10;
96+
final long maxEstimatedSize = 40;
97+
int count = 1000;
98+
for (int i = 0; i < count; i++) {
99+
control.set("k" + i, "v" + i);
100+
}
101+
102+
Cache caffeine = Caffeine.newBuilder().maximumSize(maxSize).recordStats().build();
103+
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new CaffeineClientSideCache(caffeine))) {
104+
for (int i = 0; i < count; i++) {
105+
jedis.get("k" + i);
106+
assertThat(caffeine.estimatedSize(), Matchers.lessThan(maxEstimatedSize));
107+
}
108+
}
109+
assertThat(caffeine.stats().evictionCount(), Matchers.greaterThan(count - maxEstimatedSize));
110+
}
111+
112+
@Test
113+
public void timeToLive() throws InterruptedException {
114+
int count = 1000;
115+
for (int i = 0; i < count; i++) {
116+
control.set("k" + i, "v" + i);
117+
}
118+
119+
Cache caffeine = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.SECONDS).recordStats().build();
120+
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new CaffeineClientSideCache(caffeine))) {
121+
for (int i = 0; i < count; i++) {
122+
jedis.get("k" + i);
123+
}
124+
}
125+
assertThat(caffeine.estimatedSize(), Matchers.equalTo((long) count));
126+
assertThat(caffeine.stats().evictionCount(), Matchers.equalTo(0L));
127+
128+
TimeUnit.SECONDS.sleep(2);
129+
caffeine.cleanUp();
130+
assertThat(caffeine.estimatedSize(), Matchers.equalTo(0L));
131+
assertThat(caffeine.stats().evictionCount(), Matchers.equalTo((long) count));
132+
}
133+
127134
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package redis.clients.jedis.csc;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertFalse;
5+
import static org.junit.Assert.assertTrue;
6+
7+
import java.util.ArrayList;
8+
import java.util.HashMap;
9+
import java.util.LinkedHashMap;
10+
import java.util.function.Supplier;
11+
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
12+
import org.junit.After;
13+
import org.junit.Before;
14+
import org.junit.Test;
15+
16+
import redis.clients.jedis.Connection;
17+
import redis.clients.jedis.ConnectionPoolConfig;
18+
import redis.clients.jedis.DefaultJedisClientConfig;
19+
import redis.clients.jedis.HostAndPort;
20+
import redis.clients.jedis.HostAndPorts;
21+
import redis.clients.jedis.Jedis;
22+
import redis.clients.jedis.JedisClientConfig;
23+
import redis.clients.jedis.JedisPooled;
24+
25+
public class ClientSideCacheFunctionalityTest {
26+
27+
protected static final HostAndPort hnp = HostAndPorts.getRedisServers().get(1);
28+
29+
protected Jedis control;
30+
31+
@Before
32+
public void setUp() throws Exception {
33+
control = new Jedis(hnp, DefaultJedisClientConfig.builder().password("foobared").build());
34+
control.flushAll();
35+
}
36+
37+
@After
38+
public void tearDown() throws Exception {
39+
control.close();
40+
}
41+
42+
private static final Supplier<JedisClientConfig> clientConfig
43+
= () -> DefaultJedisClientConfig.builder().resp3().password("foobared").build();
44+
45+
private static final Supplier<GenericObjectPoolConfig<Connection>> singleConnectionPoolConfig
46+
= () -> {
47+
ConnectionPoolConfig poolConfig = new ConnectionPoolConfig();
48+
poolConfig.setMaxTotal(1);
49+
return poolConfig;
50+
};
51+
52+
@Test
53+
public void flushEntireCache() {
54+
int count = 1000;
55+
for (int i = 0; i < count; i++) {
56+
control.set("k" + i, "v" + i);
57+
}
58+
59+
HashMap<Long, Object> map = new HashMap<>();
60+
ClientSideCache clientSideCache = new MapClientSideCache(map);
61+
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), clientSideCache)) {
62+
for (int i = 0; i < count; i++) {
63+
jedis.get("k" + i);
64+
}
65+
}
66+
67+
assertEquals(count, map.size());
68+
clientSideCache.clear();
69+
assertEquals(0, map.size());
70+
}
71+
72+
@Test
73+
public void removeSpecificKey() {
74+
int count = 1000;
75+
for (int i = 0; i < count; i++) {
76+
control.set("k" + i, "v" + i);
77+
}
78+
79+
// By using LinkedHashMap, we can get the hashes (map keys) at the same order of the actual keys.
80+
LinkedHashMap<Long, Object> map = new LinkedHashMap<>();
81+
ClientSideCache clientSideCache = new MapClientSideCache(map);
82+
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), clientSideCache)) {
83+
for (int i = 0; i < count; i++) {
84+
jedis.get("k" + i);
85+
}
86+
}
87+
88+
ArrayList<Long> commandHashes = new ArrayList<>(map.keySet());
89+
assertEquals(count, map.size());
90+
for (int i = 0; i < count; i++) {
91+
String key = "k" + i;
92+
Long hash = commandHashes.get(i);
93+
assertTrue(map.containsKey(hash));
94+
clientSideCache.removeKey(key);
95+
assertFalse(map.containsKey(hash));
96+
}
97+
}
98+
99+
@Test
100+
public void multiKeyOperation() {
101+
control.set("k1", "v1");
102+
control.set("k2", "v2");
103+
104+
HashMap<Long, Object> map = new HashMap<>();
105+
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new MapClientSideCache(map))) {
106+
jedis.mget("k1", "k2");
107+
assertEquals(1, map.size());
108+
}
109+
}
110+
111+
}

0 commit comments

Comments
 (0)