Skip to content

Commit 3acfced

Browse files
committed
fix: 解决 null 值在 caffeine 和 redis 中存储混乱
1 parent 4df828f commit 3acfced

1 file changed

Lines changed: 69 additions & 31 deletions

File tree

  • common/plugin/cache/src/main/java/top/cadecode/uniboot/common/plugin/cache/l2cache/cache

common/plugin/cache/src/main/java/top/cadecode/uniboot/common/plugin/cache/l2cache/cache/DLCache.java

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import cn.hutool.core.util.ObjectUtil;
44
import com.github.benmanes.caffeine.cache.Cache;
5+
import lombok.Data;
56
import lombok.Getter;
67
import lombok.extern.slf4j.Slf4j;
78
import org.springframework.cache.support.AbstractValueAdaptingCache;
@@ -58,8 +59,9 @@ public Object getNativeCache() {
5859
protected Object lookup(Object key) {
5960
String redisKey = getRedisKey(key);
6061
Object val;
61-
// 去除 toStoreValue 的包装
62-
val = fromStoreValue(caffeineCache.getIfPresent(key));
62+
val = caffeineCache.getIfPresent(key);
63+
// val 是 toStoreValue 包装过的值,为 null 则 key 不存在
64+
// 因为存储的 null 值被包装成了 DLCacheNullVal.INSTANCE
6365
if (ObjectUtil.isNotNull(val)) {
6466
log.debug("DLCache local get cache, key:{}, value:{}", key, val);
6567
return val;
@@ -101,11 +103,6 @@ public <T> T get(Object key, Callable<T> valueLoader) {
101103

102104
@Override
103105
public void put(Object key, Object value) {
104-
// val 不能为空,但传了空,就清理该 key
105-
if (!cacheProperties.isAllowNullValues() && ObjectUtil.isNull(value)) {
106-
evict(key);
107-
return;
108-
}
109106
putRemote(key, value);
110107
sendSyncMsg(key);
111108
putLocal(key, value);
@@ -119,15 +116,6 @@ public ValueWrapper putIfAbsent(Object key, Object value) {
119116
if (ObjectUtil.isNotNull(oldVal)) {
120117
return toValueWrapper(oldVal);
121118
}
122-
// 若允许值为空,且 key 存在,但值为空
123-
if (cacheProperties.isAllowNullValues() && ObjectUtil.equal(redisTemplate.hasKey(redisKey), true)) {
124-
return toValueWrapper(oldVal);
125-
}
126-
// val 不能为空,但传了空,就清理该 key
127-
if (!cacheProperties.isAllowNullValues() && ObjectUtil.isNull(value)) {
128-
evict(key);
129-
return toValueWrapper(oldVal);
130-
}
131119
Boolean setOkFlag;
132120
if (expiration > 0) {
133121
setOkFlag = redisTemplate.opsForValue().setIfAbsent(redisKey, value, expiration, TimeUnit.MILLISECONDS);
@@ -139,32 +127,28 @@ public ValueWrapper putIfAbsent(Object key, Object value) {
139127
putLocal(key, value);
140128
}
141129
return toValueWrapper(oldVal);
142-
143130
}
144131

145132
@Override
146133
public void evict(Object key) {
147134
// 先清理 redis 再清理 caffeine
148-
redisTemplate.delete(getRedisKey(key));
135+
clearRemote(key);
149136
sendSyncMsg(key);
150137
clearLocal(key);
151138
}
152139

153140
@Override
154141
public void clear() {
155142
// 先清理 redis 再清理 caffeine
156-
Set<String> keys = redisTemplate.keys(getRedisKey("*"));
157-
if (ObjectUtil.isNotEmpty(keys)) {
158-
keys.forEach(redisTemplate::delete);
159-
}
143+
clearRemote(null);
160144
sendSyncMsg(null);
161145
clearLocal(null);
162146
}
163147

164148
private void sendSyncMsg(Object key) {
165149
String syncTopic = cacheProperties.getRemote().getSyncTopic();
166150
DLCacheRefreshMsg refreshMsg = new DLCacheRefreshMsg(name, key);
167-
// 加入 SELF_MSG_MAP 防止重复处理
151+
// 加入 SELF_MSG_MAP 防止自身节点重复处理
168152
DLCacheRefreshListener.SELF_MSG_MAP.add(refreshMsg);
169153
redisTemplate.convertAndSend(syncTopic, refreshMsg);
170154
}
@@ -176,10 +160,63 @@ private void putLocal(Object key, Object value) {
176160

177161
private void putRemote(Object key, Object value) {
178162
if (expiration > 0) {
179-
redisTemplate.opsForValue().set(getRedisKey(key), value, expiration, TimeUnit.MILLISECONDS);
163+
// toStoreValue 包装 null 值
164+
redisTemplate.opsForValue().set(getRedisKey(key), toStoreValue(value), expiration, TimeUnit.MILLISECONDS);
165+
return;
166+
}
167+
redisTemplate.opsForValue().set(getRedisKey(key), toStoreValue(value));
168+
}
169+
170+
public void clearRemote(Object key) {
171+
if (ObjectUtil.isNull(key)) {
172+
Set<String> keys = redisTemplate.keys(getRedisKey("*"));
173+
if (ObjectUtil.isNotEmpty(keys)) {
174+
keys.forEach(redisTemplate::delete);
175+
}
176+
return;
177+
}
178+
redisTemplate.delete(getRedisKey(key));
179+
}
180+
181+
public void clearLocal(Object key) {
182+
if (ObjectUtil.isNull(key)) {
183+
caffeineCache.invalidateAll();
180184
return;
181185
}
182-
redisTemplate.opsForValue().set(getRedisKey(key), value);
186+
caffeineCache.invalidate(key);
187+
}
188+
189+
/**
190+
* 检查是否允许缓存 null
191+
*
192+
* @param value 缓存值
193+
* @return 不为空则 true,为空但允许则 false,否则异常
194+
*/
195+
private boolean checkValNotNull(Object value) {
196+
if (ObjectUtil.isNotNull(value)) {
197+
return true;
198+
}
199+
if (isAllowNullValues() && ObjectUtil.isNull(value)) {
200+
return false;
201+
}
202+
// val 不能为空,但传了空
203+
throw new DLCacheException("Check null val is not allowed");
204+
}
205+
206+
@Override
207+
protected Object fromStoreValue(Object storeValue) {
208+
if (isAllowNullValues() && DLCacheNullVal.INSTANCE.equals(storeValue)) {
209+
return null;
210+
}
211+
return storeValue;
212+
}
213+
214+
@Override
215+
protected Object toStoreValue(Object userValue) {
216+
if (!checkValNotNull(userValue)) {
217+
return DLCacheNullVal.INSTANCE;
218+
}
219+
return userValue;
183220
}
184221

185222
/**
@@ -190,11 +227,12 @@ private String getRedisKey(Object key) {
190227
return this.name.concat("::").concat(key.toString());
191228
}
192229

193-
public void clearLocal(Object key) {
194-
if (ObjectUtil.isNull(key)) {
195-
caffeineCache.invalidateAll();
196-
} else {
197-
caffeineCache.invalidate(key);
198-
}
230+
/**
231+
* 在缓存时代替 null 值,以区分是 key 不存在还是 val 为 null
232+
*/
233+
@Data
234+
public static class DLCacheNullVal {
235+
public static final DLCacheNullVal INSTANCE = new DLCacheNullVal();
236+
private String desc = "nullVal";
199237
}
200238
}

0 commit comments

Comments
 (0)