scalacache 记忆化异步刷新

scalacache memoization asynchronous refresh

我想在 Scala 中使用异步主动刷新进行基于 TTL 的记忆。

文档中的 ScalaCache 示例允许基于 TTL 的记忆,如下所示:

import scalacache._
import memoization._

implicit val scalaCache = ScalaCache(new MyCache())

def getUser(id: Int): User = memoize(60 seconds) {
  // Do DB lookup here...
  User(id, s"user${id}")
}

想知道在现有值的 TTL 到期后,数据库查找是否会在下一次 getUser 调用期间以同步和延迟的方式触发,或者刷新是否积极和异步地发生 - 甚至在下一次 getUser 调用之前。

如果 ScalaCache 实现是同步的,是否有替代库提供主动和异步刷新缓存的能力?

过期和刷新是密切相关但不同的机制。过期的条目被认为是陈旧的并且不能使用,所以它必须被丢弃并重新获取。符合刷新条件的条目意味着内容仍然可以使用,但数据应该重新获取,因为它可能已过时。 Guava 以 expireAfterWriterefreshAfterWrite 的名称提供了这些 TTL 策略,如果刷新时间小于过期时间,可以一起使用。

大多数缓存的设计倾向于丢弃未使用的内容。主动刷新需要一个专用线程来重新加载条目,而不管它们是否已被使用。因此,大多数缓存库本身不提供主动刷新,但可以让应用程序轻松地在顶部添加自定义。

当 Guava 中的读取检测到条目符合刷新条件时,调用者将执行该操作。刷新过程中的所有后续读取都将获得当前值。这意味着刷新是在触发它的用户线程上同步执行的,并且与读取该值的其他线程异步执行。如果覆盖 CacheLoader.reload 以在执行程序上执行工作,则刷新可能是完全异步的。

Caffeine 是对 Guava 缓存的重写,略有不同的是始终对用户线程异步执行刷新。缓存将操作委托给执行程序,默认情况下 ForkJoinPool.commonPool 这是一个 JVM 范围的执行程序。 Policy api 提供检查缓存运行时状态的方法,例如条目的年龄,以添加特定于应用程序的自定义行为。

对其他 ScalaCache 后端的支持是混合的。 Ehcache 有一个 RefreshAheadCache 装饰器,它使用自己的线程池延迟刷新。 Redis 和 memcached 不会刷新,因为它们不知道记录系统。 LruMap 嫁接了过期支持,没有任何刷新功能。