Skip to content

Commit 43e758a

Browse files
committed
Add near and remote hit and miss
1 parent 6daebdb commit 43e758a

4 files changed

Lines changed: 143 additions & 11 deletions

File tree

src/main/java/io/ebean/redis/DuelCache.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.HashSet;
77
import java.util.Map;
88
import java.util.Set;
9+
import java.util.concurrent.atomic.LongAdder;
910

1011
public class DuelCache implements ServerCache, NearCacheInvalidate {
1112

@@ -14,6 +15,11 @@ public class DuelCache implements ServerCache, NearCacheInvalidate {
1415
private final NearCacheNotify cacheNotify;
1516
private final String cacheKey;
1617

18+
private final LongAdder nearMissCount = new LongAdder();
19+
private final LongAdder nearHitCount = new LongAdder();
20+
private final LongAdder remoteMissCount = new LongAdder();
21+
private final LongAdder remoteHitCount = new LongAdder();
22+
1723
public DuelCache(ServerCache near, ServerCache remote, String cacheKey, NearCacheNotify cacheNotify) {
1824
this.near = near;
1925
this.remote = remote;
@@ -41,6 +47,9 @@ public Map<Object, Object> getAll(Set<Object> keys) {
4147

4248
Map<Object, Object> resultMap = near.getAll(keys);
4349

50+
nearHitCount.add(resultMap.size());
51+
nearMissCount.add(keys.size() - resultMap.size());
52+
4453
Set<Object> localKeys = resultMap.keySet();
4554

4655
Set<Object> remainingKeys = new HashSet<>();
@@ -52,6 +61,10 @@ public Map<Object, Object> getAll(Set<Object> keys) {
5261
if (!remainingKeys.isEmpty()) {
5362
// fetch missing ones from remote cache and merge results
5463
Map<Object, Object> remoteMap = remote.getAll(remainingKeys);
64+
65+
remoteHitCount.add(remoteMap.size());
66+
remoteMissCount.add(remainingKeys.size() - remoteMap.size());
67+
5568
if (!remoteMap.isEmpty()) {
5669
near.putAll(remoteMap);
5770
resultMap.putAll(remoteMap);
@@ -65,10 +78,15 @@ public Map<Object, Object> getAll(Set<Object> keys) {
6578
public Object get(Object id) {
6679
Object val = near.get(id);
6780
if (val != null) {
81+
nearHitCount.increment();
6882
return val;
6983
}
84+
nearMissCount.increment();
7085
Object remoteVal = remote.get(id);
71-
if (remoteVal != null) {
86+
if (remoteVal == null) {
87+
remoteMissCount.increment();
88+
} else {
89+
remoteHitCount.increment();
7290
near.put(id, remoteVal);
7391
}
7492
return remoteVal;
@@ -83,7 +101,6 @@ public void putAll(Map<Object, Object> keyValues) {
83101

84102
@Override
85103
public void put(Object id, Object value) {
86-
87104
near.put(id, value);
88105
remote.put(id, value);
89106
cacheNotify.invalidateKey(cacheKey, id);
@@ -126,4 +143,19 @@ public ServerCacheStatistics getStatistics(boolean reset) {
126143
return null;
127144
}
128145

146+
public long getNearMissCount() {
147+
return nearMissCount.longValue();
148+
}
149+
150+
public long getNearHitCount() {
151+
return nearHitCount.longValue();
152+
}
153+
154+
public long getRemoteMissCount() {
155+
return remoteMissCount.longValue();
156+
}
157+
158+
public long getRemoteHitCount() {
159+
return remoteHitCount.longValue();
160+
}
129161
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package io.ebean.redis;
2+
3+
import java.security.SecureRandom;
4+
import java.util.Base64;
5+
6+
/**
7+
* Provides a modified base64 encoded UUID and shorter 12 character random unique value.
8+
* <p>
9+
* <h3>newId()</h3>
10+
* <p>
11+
* It produces a 22 character string that is a base64 encoded UUID with the +
12+
* and / characters replaced with - and _ so as to be URL safe without requiring
13+
* encoding.
14+
* </p>
15+
* <h3>newShortId()</h3>
16+
* <p>
17+
* It produces a 12 character string that base64 encoded random number (72 bit).
18+
* </p>
19+
* <p>
20+
* Note that this now internally uses java.util.Base64 to encode the values.
21+
* </p>
22+
*/
23+
public class ModId {
24+
25+
private static final SecureRandom shortIdSecureRandom = new SecureRandom();
26+
27+
private static final Base64.Encoder urlEncoder = Base64.getUrlEncoder();
28+
29+
/**
30+
* Return a 12 character string using a 72 bit randomly generated ID encoded
31+
* in modified base64.
32+
* <p>
33+
* A UUID is 128 bits and this is 72 bits so quite a bit smaller but still
34+
* very random with one in 4.7 * 10^21 chance of a collision.
35+
* </p>
36+
*/
37+
public static String id() {
38+
39+
// Random 72 bits
40+
byte[] randomBytes = new byte[9];
41+
shortIdSecureRandom.nextBytes(randomBytes);
42+
return encode64(randomBytes);
43+
}
44+
45+
private static String encode64(byte[] bytes) {
46+
return urlEncoder.encodeToString(bytes);
47+
}
48+
49+
}

src/main/java/io/ebean/redis/RedisCacheFactory.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@
4040

4141
class RedisCacheFactory implements ServerCacheFactory {
4242

43-
private static final Logger queryLogger = LoggerFactory.getLogger("org.avaje.ebean.cache.QUERY");
43+
private static final Logger queryLogger = LoggerFactory.getLogger("io.ebean.cache.QUERY");
4444

45-
private static final Logger logger = LoggerFactory.getLogger("org.avaje.ebean.cache.CACHE");
45+
private static final Logger logger = LoggerFactory.getLogger("io.ebean.cache.CACHE");
4646

4747
private static final Logger tableModLogger = LoggerFactory.getLogger("io.ebean.cache.TABLEMODS");
4848

@@ -82,6 +82,8 @@ class RedisCacheFactory implements ServerCacheFactory {
8282
private final TimedMetric metricMsgOut;
8383
private final TimedMetric metricMsgIn;
8484

85+
private final String serverId = ModId.id();
86+
8587
private ServerCacheNotify listener;
8688

8789

@@ -276,6 +278,7 @@ public void invalidateKeys(String cacheKey, Set<Object> keySet) {
276278
try (Jedis resource = jedisPool.getResource()) {
277279
ByteArrayOutputStream ba = new ByteArrayOutputStream(100);
278280
ObjectOutputStream os = new ObjectOutputStream(ba);
281+
os.writeUTF(serverId);
279282
os.writeInt(MSG_NEARCACHE_KEYS);
280283
os.writeUTF(cacheKey);
281284
os.writeInt(keySet.size());
@@ -301,6 +304,7 @@ public void invalidateKey(String cacheKey, Object id) {
301304
try (Jedis resource = jedisPool.getResource()) {
302305
ByteArrayOutputStream ba = new ByteArrayOutputStream(100);
303306
ObjectOutputStream os = new ObjectOutputStream(ba);
307+
os.writeUTF(serverId);
304308
os.writeInt(MSG_NEARCACHE_KEY);
305309
os.writeUTF(cacheKey);
306310
os.writeObject(id);
@@ -323,6 +327,7 @@ public void invalidateClear(String cacheKey) {
323327
try (Jedis resource = jedisPool.getResource()) {
324328
ByteArrayOutputStream ba = new ByteArrayOutputStream(100);
325329
ObjectOutputStream os = new ObjectOutputStream(ba);
330+
os.writeUTF(serverId);
326331
os.writeInt(MSG_NEARCACHE_CLEAR);
327332
os.writeUTF(cacheKey);
328333
os.flush();
@@ -378,10 +383,15 @@ private void processNearCacheMessage(byte[] message) {
378383
String cacheKey = null;
379384
try {
380385
ObjectInputStream oi = new ObjectInputStream(new ByteArrayInputStream(message));
386+
String sourceServerId = oi.readUTF();
381387
int msgType = oi.readInt();
382388
cacheKey = oi.readUTF();
389+
if (sourceServerId.equals(serverId)) {
390+
// ignore this message as we are the server that sent it
391+
return;
392+
}
383393
if (logger.isDebugEnabled()) {
384-
logger.debug("processNearCacheMessage type:{} cacheKey:{}", msgType, cacheKey);
394+
logger.debug("processNearCacheMessage serverId:{} type:{} cacheKey:{}", sourceServerId, msgType, cacheKey);
385395
}
386396

387397
switch (msgType) {

src/test/java/io/ebean/redis/ClusterTest.java

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import io.ebean.config.ServerConfig;
88
import org.junit.Test;
99

10+
import static org.assertj.core.api.Assertions.assertThat;
11+
1012
public class ClusterTest {
1113

1214

@@ -23,31 +25,70 @@ private EbeanServer createOther() {
2325
}
2426

2527
@Test
26-
public void test() {
28+
public void test() throws InterruptedException {
2729

2830
EbeanServer server = Ebean.getDefaultServer();
2931

3032
EbeanServer other = createOther();
3133

3234
for (int i = 0; i < 10; i++) {
33-
EFoo foo = new EFoo("name "+i);
35+
EFoo foo = new EFoo("name " + i);
3436
foo.save();
3537
}
3638

3739

3840
other.getServerCacheManager().clearAll();
3941

42+
DuelCache dualCache = (DuelCache) other.getServerCacheManager().getBeanCache(EFoo.class);
43+
4044
EFoo foo0 = other.find(EFoo.class, 1);
41-
EFoo foo1 = other.find(EFoo.class, 1);
42-
EFoo foo2 = other.find(EFoo.class, 1);
45+
assertCounts(dualCache, 0, 1, 0, 1);
4346

44-
other.getServerCacheManager().clearAll();
47+
other.find(EFoo.class, 1);
48+
assertCounts(dualCache, 1, 1, 0, 1);
49+
50+
other.find(EFoo.class, 1);
51+
assertCounts(dualCache, 2, 1, 0, 1);
52+
53+
other.find(EFoo.class, 1);
54+
assertCounts(dualCache, 3, 1, 0, 1);
55+
56+
other.find(EFoo.class, 2);
57+
assertCounts(dualCache, 3, 2, 0, 2);
58+
59+
60+
//other.getServerCacheManager().clearAll();
4561

46-
System.out.println("done");
62+
63+
foo0.setName("name2");
64+
foo0.save();
65+
allowAsyncMessaging();
4766

4867
EFoo foo3 = other.find(EFoo.class, 1);
68+
assertThat(foo3.getName()).isEqualTo("name2");
69+
assertCounts(dualCache, 3, 3, 1, 2);
70+
71+
72+
foo0.setName("name3");
73+
foo0.save();
74+
allowAsyncMessaging();
75+
76+
foo3 = other.find(EFoo.class, 1);
77+
assertThat(foo3.getName()).isEqualTo("name3");
78+
assertCounts(dualCache, 3, 4, 2, 2);
4979

5080
System.out.println("done");
81+
}
82+
83+
private void assertCounts(DuelCache dualCache, int nearHits, int nearMiss, int remoteHit, int remoteMiss) {
84+
85+
assertThat(dualCache.getNearHitCount()).isEqualTo(nearHits);
86+
assertThat(dualCache.getNearMissCount()).isEqualTo(nearMiss);
87+
assertThat(dualCache.getRemoteHitCount()).isEqualTo(remoteHit);
88+
assertThat(dualCache.getRemoteMissCount()).isEqualTo(remoteMiss);
89+
}
5190

91+
private void allowAsyncMessaging() throws InterruptedException {
92+
Thread.sleep(200);
5293
}
5394
}

0 commit comments

Comments
 (0)