Skip to content

Commit 6daebdb

Browse files
committed
Add nearCache support
1 parent 074e026 commit 6daebdb

12 files changed

Lines changed: 541 additions & 81 deletions

File tree

pom.xml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,16 @@
2828
<dependency>
2929
<groupId>io.ebean</groupId>
3030
<artifactId>ebean</artifactId>
31-
<version>11.30.1</version>
31+
<version>11.31.1-SNAPSHOT</version>
3232
<scope>provided</scope>
3333
</dependency>
3434

35+
<dependency>
36+
<groupId>io.ebean.test</groupId>
37+
<artifactId>ebean-test-config</artifactId>
38+
<version>11.29.1</version>
39+
</dependency>
40+
3541
<dependency>
3642
<groupId>org.avaje.composite</groupId>
3743
<artifactId>junit</artifactId>
@@ -46,7 +52,6 @@
4652
<scope>test</scope>
4753
</dependency>
4854

49-
5055
</dependencies>
5156

5257
<build>
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package io.ebean.redis;
2+
3+
import io.ebean.cache.ServerCache;
4+
import io.ebean.cache.ServerCacheStatistics;
5+
6+
import java.util.HashSet;
7+
import java.util.Map;
8+
import java.util.Set;
9+
10+
public class DuelCache implements ServerCache, NearCacheInvalidate {
11+
12+
private final ServerCache near;
13+
private final ServerCache remote;
14+
private final NearCacheNotify cacheNotify;
15+
private final String cacheKey;
16+
17+
public DuelCache(ServerCache near, ServerCache remote, String cacheKey, NearCacheNotify cacheNotify) {
18+
this.near = near;
19+
this.remote = remote;
20+
this.cacheKey = cacheKey;
21+
this.cacheNotify = cacheNotify;
22+
}
23+
24+
@Override
25+
public void invalidateKeys(Set<Object> keySet) {
26+
near.removeAll(keySet);
27+
}
28+
29+
@Override
30+
public void invalidateKey(Object id) {
31+
near.remove(id);
32+
}
33+
34+
@Override
35+
public void invalidateClear() {
36+
near.clear();
37+
}
38+
39+
@Override
40+
public Map<Object, Object> getAll(Set<Object> keys) {
41+
42+
Map<Object, Object> resultMap = near.getAll(keys);
43+
44+
Set<Object> localKeys = resultMap.keySet();
45+
46+
Set<Object> remainingKeys = new HashSet<>();
47+
for (Object key : keys) {
48+
if (!localKeys.contains(key)) {
49+
remainingKeys.add(key);
50+
}
51+
}
52+
if (!remainingKeys.isEmpty()) {
53+
// fetch missing ones from remote cache and merge results
54+
Map<Object, Object> remoteMap = remote.getAll(remainingKeys);
55+
if (!remoteMap.isEmpty()) {
56+
near.putAll(remoteMap);
57+
resultMap.putAll(remoteMap);
58+
}
59+
}
60+
61+
return resultMap;
62+
}
63+
64+
@Override
65+
public Object get(Object id) {
66+
Object val = near.get(id);
67+
if (val != null) {
68+
return val;
69+
}
70+
Object remoteVal = remote.get(id);
71+
if (remoteVal != null) {
72+
near.put(id, remoteVal);
73+
}
74+
return remoteVal;
75+
}
76+
77+
@Override
78+
public void putAll(Map<Object, Object> keyValues) {
79+
near.putAll(keyValues);
80+
remote.putAll(keyValues);
81+
cacheNotify.invalidateKeys(cacheKey, keyValues.keySet());
82+
}
83+
84+
@Override
85+
public void put(Object id, Object value) {
86+
87+
near.put(id, value);
88+
remote.put(id, value);
89+
cacheNotify.invalidateKey(cacheKey, id);
90+
}
91+
92+
@Override
93+
public void removeAll(Set<Object> keys) {
94+
95+
near.removeAll(keys);
96+
remote.removeAll(keys);
97+
cacheNotify.invalidateKeys(cacheKey, keys);
98+
}
99+
100+
@Override
101+
public void remove(Object id) {
102+
near.remove(id);
103+
remote.remove(id);
104+
cacheNotify.invalidateKey(cacheKey, id);
105+
}
106+
107+
@Override
108+
public void clear() {
109+
near.clear();
110+
remote.clear();
111+
cacheNotify.invalidateClear(cacheKey);
112+
}
113+
114+
@Override
115+
public int size() {
116+
return 0;
117+
}
118+
119+
@Override
120+
public int getHitRatio() {
121+
return 0;
122+
}
123+
124+
@Override
125+
public ServerCacheStatistics getStatistics(boolean reset) {
126+
return null;
127+
}
128+
129+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.ebean.redis;
2+
3+
import java.util.Set;
4+
5+
/**
6+
* Near cache invalidation.
7+
*/
8+
public interface NearCacheInvalidate {
9+
10+
/**
11+
* Invalidate from near cache the given keys.
12+
*/
13+
void invalidateKeys(Set<Object> keySet);
14+
15+
/**
16+
* Invalidate from near cache the given key.
17+
*/
18+
void invalidateKey(Object id);
19+
20+
/**
21+
* Clear the near cache.
22+
*/
23+
void invalidateClear();
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.ebean.redis;
2+
3+
import java.util.Set;
4+
5+
/**
6+
* Notify other cluster members to invalidate parts of their near cache.
7+
*/
8+
public interface NearCacheNotify {
9+
10+
/**
11+
* Invalidate the given keys.
12+
*/
13+
void invalidateKeys(String cacheKey, Set<Object> keySet);
14+
15+
/**
16+
* Invalidate a single key.
17+
*/
18+
void invalidateKey(String cacheKey, Object id);
19+
20+
/**
21+
* Clear a near cache.
22+
*/
23+
void invalidateClear(String cacheKey);
24+
}

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

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,17 @@ class RedisCache implements ServerCache {
4343
this.keyEncode = keyEncode;
4444
this.valueEncode = valueEncode;
4545

46-
String prefix = "ebean.l2cache." + cacheKey;
47-
48-
metricGet = MetricFactory.get().createTimedMetric(MetricType.L2, prefix + ".get");
49-
metricGetAll = MetricFactory.get().createTimedMetric(MetricType.L2, prefix + ".getAll");
50-
metricPut = MetricFactory.get().createTimedMetric(MetricType.L2, prefix + ".put");
51-
metricPutAll = MetricFactory.get().createTimedMetric(MetricType.L2, prefix + ".putAll");
52-
metricRemove = MetricFactory.get().createTimedMetric(MetricType.L2, prefix + ".remove");
53-
metricRemoveAll = MetricFactory.get().createTimedMetric(MetricType.L2, prefix + ".removeAll");
54-
metricClear = MetricFactory.get().createTimedMetric(MetricType.L2, prefix + ".clear");
46+
String prefix = "ebean.l2cache.";
47+
48+
MetricFactory metricFactory = MetricFactory.get();
49+
50+
metricGet = metricFactory.createTimedMetric(MetricType.L2, prefix + "get." + cacheKey);
51+
metricGetAll = metricFactory.createTimedMetric(MetricType.L2, prefix + "getMany." + cacheKey);
52+
metricPut = metricFactory.createTimedMetric(MetricType.L2, prefix + "put." + cacheKey);
53+
metricPutAll = metricFactory.createTimedMetric(MetricType.L2, prefix + "putMany." + cacheKey);
54+
metricRemove = metricFactory.createTimedMetric(MetricType.L2, prefix + "remove." + cacheKey);
55+
metricRemoveAll = metricFactory.createTimedMetric(MetricType.L2, prefix + "removeMany." + cacheKey);
56+
metricClear = metricFactory.createTimedMetric(MetricType.L2, prefix + "clear." + cacheKey);
5557
}
5658

5759
private byte[] key(Object id) {
@@ -84,18 +86,17 @@ public Map<Object, Object> getAll(Set<Object> keys) {
8486
for (int i = 0; i < keyList.size(); i++) {
8587
map.put(keyList.get(i), valueDecode(valsAsBytes.get(i)));
8688
}
87-
metricGetAll.add((System.nanoTime() - start) / 1000L, keyList.size());
89+
metricGetAll.addSinceNanos(start, keyList.size());
8890
return map;
8991
}
9092
}
9193

92-
9394
@Override
9495
public Object get(Object id) {
9596
long start = System.nanoTime();
9697
try (Jedis resource = jedisPool.getResource()) {
9798
Object val = valueDecode(resource.get(key(id)));
98-
metricGet.add((System.nanoTime() - start) / 1000L);
99+
metricGet.addSinceNanos(start);
99100
return val;
100101
}
101102
}
@@ -106,7 +107,7 @@ public void put(Object id, Object value) {
106107
long start = System.nanoTime();
107108
try (Jedis resource = jedisPool.getResource()) {
108109
resource.set(key(id), value(value));
109-
metricPut.add((System.nanoTime() - start) / 1000L);
110+
metricPut.addSinceNanos(start);
110111
}
111112
}
112113

@@ -121,7 +122,7 @@ public void putAll(Map<Object, Object> keyValues) {
121122
raw[pos++] = value(entry.getValue());
122123
}
123124
resource.mset(raw);
124-
metricPutAll.add((System.nanoTime() - start) / 1000L, keyValues.size());
125+
metricPutAll.addSinceNanos(start, keyValues.size());
125126
}
126127
}
127128

@@ -130,7 +131,7 @@ public void remove(Object id) {
130131
long start = System.nanoTime();
131132
try (Jedis resource = jedisPool.getResource()) {
132133
resource.del(key(id));
133-
metricRemove.add((System.nanoTime() - start) / 1000L);
134+
metricRemove.addSinceNanos(start);
134135
}
135136
}
136137

@@ -139,7 +140,7 @@ public void removeAll(Set<Object> keys) {
139140
long start = System.nanoTime();
140141
try (Jedis resource = jedisPool.getResource()) {
141142
resource.del(keysAsBytes(keys));
142-
metricRemoveAll.add((System.nanoTime() - start) / 1000L, keys.size());
143+
metricRemoveAll.addSinceNanos(start, keys.size());
143144
}
144145
}
145146

@@ -167,16 +168,18 @@ public void clear() {
167168
List<byte[]> keys = scanResult.getResult();
168169
nextCursor = scanResult.getCursorAsBytes();
169170

170-
byte[][] raw = new byte[keys.size()][];
171-
for (int i = 0; i < keys.size(); i++) {
172-
raw[i] = keys.get(i);
171+
if (!keys.isEmpty()) {
172+
byte[][] raw = new byte[keys.size()][];
173+
for (int i = 0; i < keys.size(); i++) {
174+
raw[i] = keys.get(i);
175+
}
176+
resource.del(raw);
173177
}
174-
resource.del(raw);
175178

176179
next = SafeEncoder.encode(nextCursor);
177180
} while (!next.equals("0"));
178181

179-
metricClear.add((System.nanoTime() - start) / 1000L);
182+
metricClear.addSinceNanos(start);
180183
}
181184
}
182185

0 commit comments

Comments
 (0)