Skip to content

Commit bd852ed

Browse files
authored
Merge 000e07b into 333dcd7
2 parents 333dcd7 + 000e07b commit bd852ed

File tree

10 files changed

+188
-69
lines changed

10 files changed

+188
-69
lines changed

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

+7-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.concurrent.ConcurrentHashMap;
99
import java.util.function.Function;
1010
import redis.clients.jedis.CommandObject;
11+
import redis.clients.jedis.csc.hash.CommandLongHashing;
1112
import redis.clients.jedis.util.SafeEncoder;
1213

1314
/**
@@ -21,13 +22,15 @@ public abstract class ClientSideCache {
2122
protected static final int DEFAULT_EXPIRE_SECONDS = 100;
2223

2324
private final Map<ByteBuffer, Set<Long>> keyToCommandHashes = new ConcurrentHashMap<>();
25+
private final CommandLongHashing commandHashing;
2426
private final ClientSideCacheable cacheable;
2527

26-
protected ClientSideCache() {
27-
this.cacheable = DefaultClientSideCacheable.INSTANCE;
28+
protected ClientSideCache(CommandLongHashing commandHashing) {
29+
this(commandHashing, DefaultClientSideCacheable.INSTANCE);
2830
}
2931

30-
protected ClientSideCache(ClientSideCacheable cacheable) {
32+
protected ClientSideCache(CommandLongHashing commandHashing, ClientSideCacheable cacheable) {
33+
this.commandHashing = commandHashing;
3134
this.cacheable = cacheable;
3235
}
3336

@@ -39,8 +42,6 @@ protected ClientSideCache(ClientSideCacheable cacheable) {
3942

4043
protected abstract Object getValue(long hash);
4144

42-
protected abstract long getHash(CommandObject command);
43-
4445
public final void clear() {
4546
invalidateAllKeysAndCommandHashes();
4647
}
@@ -79,7 +80,7 @@ public final <T> T get(Function<CommandObject<T>, T> loader, CommandObject<T> co
7980
return loader.apply(command);
8081
}
8182

82-
final long hash = getHash(command);
83+
final long hash = commandHashing.hash(command);
8384

8485
T value = (T) getValue(hash);
8586
if (value != null) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package redis.clients.jedis.csc.hash;
2+
3+
import redis.clients.jedis.Builder;
4+
import redis.clients.jedis.CommandObject;
5+
import redis.clients.jedis.args.Rawable;
6+
7+
public abstract class AbstractCommandHashing implements CommandLongHashing {
8+
9+
@Override
10+
public final long hash(CommandObject command) {
11+
long[] nums = new long[command.getArguments().size() + 1];
12+
int idx = 0;
13+
for (Rawable raw : command.getArguments()) {
14+
nums[idx++] = hashRawable(raw);
15+
}
16+
nums[idx] = hashBuilder(command.getBuilder());
17+
return hashLongs(nums);
18+
}
19+
20+
protected abstract long hashLongs(long[] longs);
21+
22+
protected abstract long hashRawable(Rawable raw);
23+
24+
protected long hashBuilder(Builder builder) {
25+
return builder.hashCode();
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package redis.clients.jedis.csc.hash;
2+
3+
import redis.clients.jedis.CommandObject;
4+
5+
/**
6+
* The interface for hashing a command object for client-side caching.
7+
*/
8+
public interface CommandLongHashing {
9+
10+
/**
11+
* Produce a 64-bit signed hash from a command object.
12+
* @param command the command object
13+
* @return 64-bit signed hash
14+
*/
15+
long hash(CommandObject command);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package redis.clients.jedis.csc.hash;
2+
3+
import com.google.common.hash.HashFunction;
4+
import com.google.common.hash.Hasher;
5+
import redis.clients.jedis.CommandObject;
6+
7+
public class GuavaHashing implements CommandLongHashing {
8+
9+
public static final HashFunction DEFAULT_HASH_FUNCTION = com.google.common.hash.Hashing.fingerprint2011();
10+
11+
private final HashFunction function;
12+
13+
public GuavaHashing(HashFunction function) {
14+
this.function = function;
15+
}
16+
17+
@Override
18+
public long hash(CommandObject command) {
19+
Hasher hasher = function.newHasher();
20+
command.getArguments().forEach(raw -> hasher.putBytes(raw.getRaw()));
21+
hasher.putInt(command.getBuilder().hashCode());
22+
return hasher.hash().asLong();
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package redis.clients.jedis.csc.hash;
2+
3+
import net.openhft.hashing.LongHashFunction;
4+
5+
public class OpenHftHashing extends PrimitiveArrayHashing implements CommandLongHashing {
6+
7+
public static final LongHashFunction DEFAULT_HASH_FUNCTION = LongHashFunction.xx3();
8+
9+
private final LongHashFunction function;
10+
11+
public OpenHftHashing(LongHashFunction function) {
12+
this.function = function;
13+
}
14+
15+
@Override
16+
protected long hashLongs(long[] longs) {
17+
return function.hashLongs(longs);
18+
}
19+
20+
@Override
21+
protected long hashBytes(byte[] bytes) {
22+
return function.hashBytes(bytes);
23+
}
24+
25+
@Override
26+
protected long hashInt(int hashCode) {
27+
return function.hashInt(hashCode);
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package redis.clients.jedis.csc.hash;
2+
3+
import redis.clients.jedis.Builder;
4+
import redis.clients.jedis.args.Rawable;
5+
6+
public abstract class PrimitiveArrayHashing extends AbstractCommandHashing {
7+
8+
@Override
9+
protected final long hashRawable(Rawable raw) {
10+
return hashBytes(raw.getRaw());
11+
}
12+
13+
@Override
14+
protected final long hashBuilder(Builder builder) {
15+
return hashInt(builder.hashCode());
16+
}
17+
18+
protected abstract long hashBytes(byte[] bytes);
19+
20+
protected long hashInt(int hashCode) {
21+
return hashCode;
22+
}
23+
}

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

+17-28
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,28 @@
33
import com.github.benmanes.caffeine.cache.Cache;
44
import com.github.benmanes.caffeine.cache.Caffeine;
55
import java.util.concurrent.TimeUnit;
6-
import net.openhft.hashing.LongHashFunction;
76

8-
import redis.clients.jedis.CommandObject;
9-
import redis.clients.jedis.args.Rawable;
107
import redis.clients.jedis.csc.ClientSideCache;
118
import redis.clients.jedis.csc.ClientSideCacheable;
129
import redis.clients.jedis.csc.DefaultClientSideCacheable;
10+
import redis.clients.jedis.csc.hash.CommandLongHashing;
11+
import redis.clients.jedis.csc.hash.OpenHftHashing;
1312

1413
public class CaffeineCSC extends ClientSideCache {
1514

16-
private static final LongHashFunction DEFAULT_HASH_FUNCTION = LongHashFunction.xx3();
17-
1815
private final Cache<Long, Object> cache;
19-
private final LongHashFunction hashFunction;
2016

21-
public CaffeineCSC(Cache<Long, Object> caffeineCache, LongHashFunction hashFunction) {
22-
super();
23-
this.cache = caffeineCache;
24-
this.hashFunction = hashFunction;
17+
public CaffeineCSC(Cache<Long, Object> caffeineCache) {
18+
this(caffeineCache, new OpenHftHashing(OpenHftHashing.DEFAULT_HASH_FUNCTION), DefaultClientSideCacheable.INSTANCE);
19+
}
20+
21+
public CaffeineCSC(Cache<Long, Object> caffeineCache, ClientSideCacheable cacheable) {
22+
this(caffeineCache, new OpenHftHashing(OpenHftHashing.DEFAULT_HASH_FUNCTION), cacheable);
2523
}
2624

27-
public CaffeineCSC(Cache<Long, Object> caffeineCache, LongHashFunction function, ClientSideCacheable cacheable) {
28-
super(cacheable);
25+
public CaffeineCSC(Cache<Long, Object> caffeineCache, CommandLongHashing hashing, ClientSideCacheable cacheable) {
26+
super(hashing, cacheable);
2927
this.cache = caffeineCache;
30-
this.hashFunction = function;
3128
}
3229

3330
@Override
@@ -50,17 +47,6 @@ protected Object getValue(long hash) {
5047
return cache.getIfPresent(hash);
5148
}
5249

53-
@Override
54-
protected final long getHash(CommandObject command) {
55-
long[] nums = new long[command.getArguments().size() + 1];
56-
int idx = 0;
57-
for (Rawable raw : command.getArguments()) {
58-
nums[idx++] = hashFunction.hashBytes(raw.getRaw());
59-
}
60-
nums[idx] = hashFunction.hashInt(command.getBuilder().hashCode());
61-
return hashFunction.hashLongs(nums);
62-
}
63-
6450
public static Builder builder() {
6551
return new Builder();
6652
}
@@ -71,7 +57,8 @@ public static class Builder {
7157
private long expireTime = DEFAULT_EXPIRE_SECONDS;
7258
private final TimeUnit expireTimeUnit = TimeUnit.SECONDS;
7359

74-
private LongHashFunction hashFunction = DEFAULT_HASH_FUNCTION;
60+
// not using a default value to avoid an object creation like 'new OpenHftHashing(hashFunction)'
61+
private CommandLongHashing longHashing = null;
7562

7663
private ClientSideCacheable cacheable = DefaultClientSideCacheable.INSTANCE;
7764

@@ -87,8 +74,8 @@ public Builder ttl(int seconds) {
8774
return this;
8875
}
8976

90-
public Builder hashFunction(LongHashFunction function) {
91-
this.hashFunction = function;
77+
public Builder hashing(CommandLongHashing hashing) {
78+
this.longHashing = hashing;
9279
return this;
9380
}
9481

@@ -104,7 +91,9 @@ public CaffeineCSC build() {
10491

10592
cb.expireAfterWrite(expireTime, expireTimeUnit);
10693

107-
return new CaffeineCSC(cb.build(), hashFunction, cacheable);
94+
return longHashing != null
95+
? new CaffeineCSC(cb.build(), longHashing, cacheable)
96+
: new CaffeineCSC(cb.build(), cacheable);
10897
}
10998
}
11099
}

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

+24-22
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,38 @@
33
import com.google.common.cache.Cache;
44
import com.google.common.cache.CacheBuilder;
55
import com.google.common.hash.HashFunction;
6-
import com.google.common.hash.Hasher;
76
import java.util.concurrent.TimeUnit;
87

9-
import redis.clients.jedis.CommandObject;
108
import redis.clients.jedis.csc.ClientSideCache;
119
import redis.clients.jedis.csc.ClientSideCacheable;
1210
import redis.clients.jedis.csc.DefaultClientSideCacheable;
11+
import redis.clients.jedis.csc.hash.CommandLongHashing;
12+
import redis.clients.jedis.csc.hash.GuavaHashing;
1313

1414
public class GuavaCSC extends ClientSideCache {
1515

16-
private static final HashFunction DEFAULT_HASH_FUNCTION = com.google.common.hash.Hashing.fingerprint2011();
17-
1816
private final Cache<Long, Object> cache;
19-
private final HashFunction hashFunction;
2017

2118
public GuavaCSC(Cache<Long, Object> guavaCache) {
22-
this(guavaCache, DEFAULT_HASH_FUNCTION);
19+
this(guavaCache, GuavaHashing.DEFAULT_HASH_FUNCTION);
2320
}
2421

2522
public GuavaCSC(Cache<Long, Object> guavaCache, HashFunction hashFunction) {
26-
super();
23+
this(guavaCache, new GuavaHashing(hashFunction));
24+
}
25+
26+
public GuavaCSC(Cache<Long, Object> guavaCache, CommandLongHashing hashing) {
27+
super(hashing);
2728
this.cache = guavaCache;
28-
this.hashFunction = hashFunction;
2929
}
3030

3131
public GuavaCSC(Cache<Long, Object> guavaCache, ClientSideCacheable cacheable) {
32-
this(guavaCache, DEFAULT_HASH_FUNCTION, cacheable);
32+
this(guavaCache, new GuavaHashing(GuavaHashing.DEFAULT_HASH_FUNCTION), cacheable);
3333
}
3434

35-
public GuavaCSC(Cache<Long, Object> cache, HashFunction function, ClientSideCacheable cacheable) {
36-
super(cacheable);
35+
public GuavaCSC(Cache<Long, Object> cache, CommandLongHashing hashing, ClientSideCacheable cacheable) {
36+
super(hashing, cacheable);
3737
this.cache = cache;
38-
this.hashFunction = function;
3938
}
4039

4140
@Override
@@ -58,14 +57,6 @@ protected Object getValue(long hash) {
5857
return cache.getIfPresent(hash);
5958
}
6059

61-
@Override
62-
protected final long getHash(CommandObject command) {
63-
Hasher hasher = hashFunction.newHasher();
64-
command.getArguments().forEach(raw -> hasher.putBytes(raw.getRaw()));
65-
hasher.putInt(command.getBuilder().hashCode());
66-
return hasher.hash().asLong();
67-
}
68-
6960
public static Builder builder() {
7061
return new Builder();
7162
}
@@ -76,7 +67,9 @@ public static class Builder {
7667
private long expireTime = DEFAULT_EXPIRE_SECONDS;
7768
private final TimeUnit expireTimeUnit = TimeUnit.SECONDS;
7869

79-
private HashFunction hashFunction = DEFAULT_HASH_FUNCTION;
70+
// not using a default value to avoid an object creation like 'new GuavaHashing(hashFunction)'
71+
private HashFunction hashFunction = null;
72+
private CommandLongHashing longHashing = null;
8073

8174
private ClientSideCacheable cacheable = DefaultClientSideCacheable.INSTANCE;
8275

@@ -94,6 +87,13 @@ public Builder ttl(int seconds) {
9487

9588
public Builder hashFunction(HashFunction function) {
9689
this.hashFunction = function;
90+
this.longHashing = null;
91+
return this;
92+
}
93+
94+
public Builder hashing(CommandLongHashing hashing) {
95+
this.longHashing = hashing;
96+
this.hashFunction = null;
9797
return this;
9898
}
9999

@@ -109,7 +109,9 @@ public GuavaCSC build() {
109109

110110
cb.expireAfterWrite(expireTime, expireTimeUnit);
111111

112-
return new GuavaCSC(cb.build(), hashFunction, cacheable);
112+
return longHashing != null ? new GuavaCSC(cb.build(), longHashing)
113+
: hashFunction != null ? new GuavaCSC(cb.build(), hashFunction)
114+
: new GuavaCSC(cb.build());
113115
}
114116
}
115117
}

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import redis.clients.jedis.Jedis;
2424
import redis.clients.jedis.JedisClientConfig;
2525
import redis.clients.jedis.JedisPooled;
26+
import redis.clients.jedis.csc.hash.OpenHftHashing;
2627
import redis.clients.jedis.csc.util.CaffeineCSC;
2728
import redis.clients.jedis.csc.util.GuavaCSC;
2829

@@ -93,7 +94,8 @@ public void guavaMore() {
9394

9495
@Test
9596
public void caffeineSimple() {
96-
CaffeineCSC caffeine = CaffeineCSC.builder().maximumSize(10).ttl(10).hashFunction(LongHashFunction.xx()).build();
97+
CaffeineCSC caffeine = CaffeineCSC.builder().maximumSize(10).ttl(10)
98+
.hashing(new OpenHftHashing(LongHashFunction.xx())).build();
9799
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), caffeine)) {
98100
control.set("foo", "bar");
99101
assertEquals("bar", jedis.get("foo"));
@@ -107,8 +109,8 @@ public void caffeineMore() {
107109

108110
com.github.benmanes.caffeine.cache.Cache caffeine = Caffeine.newBuilder().recordStats().build();
109111

110-
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(),
111-
new CaffeineCSC(caffeine, LongHashFunction.city_1_1()), singleConnectionPoolConfig.get())) {
112+
try (JedisPooled jedis = new JedisPooled(hnp, clientConfig.get(), new CaffeineCSC(caffeine),
113+
singleConnectionPoolConfig.get())) {
112114
control.set("foo", "bar");
113115
assertEquals(0, caffeine.estimatedSize());
114116
assertEquals("bar", jedis.get("foo"));

0 commit comments

Comments
 (0)