基于谓词的计划内存缓存失效
Scheduled in-memory cache invalidation based on predicate
我有 List<CompletableFuture>
存储在 Map
private final Map<UUID, List<CompletableFuture>> hydrationProcesses = new ConcurrentHashMap<>();
目前有一个守护线程每 30 秒运行一次,并删除所有已经完成的 Futures
。
有很多 TTL 失效实现,我正在寻找基于某些谓词的失效。我想摆脱这个守护线程。
是否有针对基于某些自定义逻辑的计划缓存失效的开箱即用解决方案。也许我错过了 Spring/Guava 中的某些内容?
可能类似于 Guava 的
CacheBuilder.newBuilder()
.expireAfterAccess(2,TimeUnit.MILLISECONDS)
.build(loader);
而是在访问后将所有内容标记为过期,我需要检查这个 Future 是否已经完成,然后将其从缓存中删除。
我怀疑是否有基于 Guava 的解决方案。由于您没有特别要求“仅限 Guava”的解决方案,我想提供一个想法如何解决 cache2k.
的问题
设置可能如下所示:
final long RECHECK_INTERVAL_MILLIS = 30000;
Cache<UUID, CompletableFuture<Void>> cache =
new Cache2kBuilder<UUID, CompletableFuture<Void>>(){}
.loader(new AdvancedCacheLoader<UUID, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> load(UUID key,
long currentTime,
CacheEntry<UUID, CompletableFuture<Void>> currentEntry) {
return currentEntry != null ? currentEntry.getValue() : null;
}
})
.expiryPolicy(new ExpiryPolicy<UUID, CompletableFuture<Void>>() {
@Override
public long calculateExpiryTime(UUID key,
CompletableFuture<Void> value,
long loadTime,
CacheEntry<UUID, CompletableFuture<Void>> oldEntry) {
return value.isDone() ? NOW : loadTime + RECHECK_INTERVAL_MILLIS;
}
})
.refreshAhead(true)
.build();
实际上 cache2k 具有与 Guava 或其他缓存类似的功能。然而,有一些微小的扩展允许更复杂的设置。
这里使用的技巧是在读取操作中配置缓存,但使加载器 return 成为当前缓存值。当一个条目过期时,加载程序被调用,因为 refreshAhead(true)
,但当前值被保留并再次评估过期策略。您希望检查的谓词进入过期策略。
其他缓存也有通读和自定义过期时间,但缺乏可以根据现有缓存值更有效地运行的“更智能加载器”(AdvancedCacheLoader
) 的概念。
我们在生产中使用与此类似的设置。
也有缺点。如果使用自定义到期时间,cache2k 对每个缓存使用一个计时线程。这意味着您的额外线程不会消失。 cache2k 将在未来得到增强,以拥有一组在所有缓存之间共享的用于计时的全局线程。 更新: 新版本的 cache2k 为计时器使用一个通用线程池。
免责声明:我是 cache2k 的作者,所以我不能绝对肯定地说没有基于 Guava 的可能解决方案。
我有 List<CompletableFuture>
存储在 Map
private final Map<UUID, List<CompletableFuture>> hydrationProcesses = new ConcurrentHashMap<>();
目前有一个守护线程每 30 秒运行一次,并删除所有已经完成的 Futures
。
有很多 TTL 失效实现,我正在寻找基于某些谓词的失效。我想摆脱这个守护线程。
是否有针对基于某些自定义逻辑的计划缓存失效的开箱即用解决方案。也许我错过了 Spring/Guava 中的某些内容?
可能类似于 Guava 的
CacheBuilder.newBuilder()
.expireAfterAccess(2,TimeUnit.MILLISECONDS)
.build(loader);
而是在访问后将所有内容标记为过期,我需要检查这个 Future 是否已经完成,然后将其从缓存中删除。
我怀疑是否有基于 Guava 的解决方案。由于您没有特别要求“仅限 Guava”的解决方案,我想提供一个想法如何解决 cache2k.
的问题设置可能如下所示:
final long RECHECK_INTERVAL_MILLIS = 30000;
Cache<UUID, CompletableFuture<Void>> cache =
new Cache2kBuilder<UUID, CompletableFuture<Void>>(){}
.loader(new AdvancedCacheLoader<UUID, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> load(UUID key,
long currentTime,
CacheEntry<UUID, CompletableFuture<Void>> currentEntry) {
return currentEntry != null ? currentEntry.getValue() : null;
}
})
.expiryPolicy(new ExpiryPolicy<UUID, CompletableFuture<Void>>() {
@Override
public long calculateExpiryTime(UUID key,
CompletableFuture<Void> value,
long loadTime,
CacheEntry<UUID, CompletableFuture<Void>> oldEntry) {
return value.isDone() ? NOW : loadTime + RECHECK_INTERVAL_MILLIS;
}
})
.refreshAhead(true)
.build();
实际上 cache2k 具有与 Guava 或其他缓存类似的功能。然而,有一些微小的扩展允许更复杂的设置。
这里使用的技巧是在读取操作中配置缓存,但使加载器 return 成为当前缓存值。当一个条目过期时,加载程序被调用,因为 refreshAhead(true)
,但当前值被保留并再次评估过期策略。您希望检查的谓词进入过期策略。
其他缓存也有通读和自定义过期时间,但缺乏可以根据现有缓存值更有效地运行的“更智能加载器”(AdvancedCacheLoader
) 的概念。
我们在生产中使用与此类似的设置。
也有缺点。如果使用自定义到期时间,cache2k 对每个缓存使用一个计时线程。这意味着您的额外线程不会消失。 cache2k 将在未来得到增强,以拥有一组在所有缓存之间共享的用于计时的全局线程。 更新: 新版本的 cache2k 为计时器使用一个通用线程池。
免责声明:我是 cache2k 的作者,所以我不能绝对肯定地说没有基于 Guava 的可能解决方案。