Caffeine LoadingCache - 使用自定义过期策略驱逐

问题描述

使用 Caffeine 2.8.1 和 Java 8。 我已经创建了 LoadingCache<String,Boolean>。我正在使用 cache.putAll(getAllkeyvalues()) 加载缓存,其中 getAllkeyvalues() 返回 Map<String,Boolean>

我指定了一个 CacheLoader 来通过调用 keyExistsOnServer(key) 方法来计算值,该方法调用外部服务来获取值。

我已指定自定义过期策略,如果在创建/更新缓存中的条目时将该值设置为 false,我想在 10 分钟内从缓存中逐出该条目。如果值为 Long.MAX_VALUE,则将其保持为 true

  private RemovalListener<String,Boolean> removeListener =
      (key,value,cause) ->
          logger.info(
              "Entry for Key={} with value={} was removed ({}) from cache",key,cause);
  private LoadingCache<String,Boolean> cache =
      caffeine.newBuilder()
          .expireAfter(
              new Expiry<String,Boolean>() {
                private static final long EXPIRATION_TEN_MINS_IN_NANOSECONDS = 600000000000L;

                @Override
                public long expireAfterCreate(String key,Boolean value,long currentTime) {
                  // If value is false that means key does not exist,so set expiration to 10 mins
                  return value ? Long.MAX_VALUE : EXPIRATION_TEN_MINS_IN_NANOSECONDS;
                }

                @Override
                public long expireAfterUpdate(
                    String key,long currentTime,long currentDuration) {
                  // If value is changed from true to false then set expiration to 10 mins
                  return value ? Long.MAX_VALUE : EXPIRATION_TEN_MINS_IN_NANOSECONDS;
                }

                @Override
                public long expireAfterRead(
                    String key,long currentDuration) {
                  // Don't modify expiration time after read
                  return currentDuration;
                }
              })
          .recordStats()
          .removalListener(removeListener)
          .build(key -> keyExistsOnServer(key));


问题#1:根据我想要实现的目标,我的到期政策看起来是否正确?

问题#2:

我没有看到根据驱逐政策调用 RemovalListener。这可能是由于本 github issue 中所述的清理任务的累积。

但是,我的代码的正确性取决于这样一个事实,即一旦条目的过期时间(false 值的情况下为 10 分钟)已经过去,并且如果我们调用 cache.get(key),那么它不应返回缓存中的过期值,而是调用 CacheLoaderkeyExistsOnServer(key) 方法获取值。有人可以断言这就是它的行为方式吗?

以下是@Louis Wasserman 在 github 问题上的说明,但我不清楚这是否说明了这一点:

实际上发生的事情是get调用本身发现该条目已过期并将其添加到要清理的条目队列中

问题#3:如果在调用 {{1} 时 RuntimeExceptionkeyExistsOnServer(key) 方法抛出 CacheLoader 会发生什么}}?

问题#4cache.get(key) 是否包含已过期但由于清理累积而未被驱逐的条目?

问题#5:当我在 cache.asMap() 方法中记录 currentDuration 时,它似乎与经过的时间不一致。即如果它被设置为 600000000000(10 分钟)并更新为 expireAfterRead() 我希望在 5 分钟后它应该是 300000000000 但它不是。为什么会这样?

解决方法

问题#1:根据我想要实现的目标,我的到期政策看起来是否正确?

是的,这看起来不错。您可以将常量设为 Duration 或使用 TimeUnit 进行计算,只是为了让它看起来更漂亮。

问题#2:根据驱逐政策,我没有看到 RemovalListener 被调用。

当缓存上发生足够多的活动时,就会发生这种情况。您可以指定一个 Scheduler,它会根据下一个条目的过期时间来唤醒并为您调用 cache.cleanUp。对于 Java 9+ 用户,Java 提供了一个内置的调度线程,您可以通过在构建缓存时添加 Caffeine.scheduler(Scheduler.systemScheduler) 来利用该线程。

一旦一个条目的过期时间已经过去,如果我们调用 cache.get(key) 那么它就不应该从缓存中返回过期的值,而是调用 CacheLoader。

正确。缓存将在查找时验证条目,如果过期,则重新加载它。它永远不会返回过期的条目。

问题#3:如果调用 cache.get(key) 方法时 CacheLoader 抛出 RuntimeException 会发生什么?

映射不会建立,异常会传播给调用者。对 get(key) 的下一次调用将重新尝试,这可能会再次失败。您可以选择如何对此保持弹性。通常不是这样是一个很好的答案,或者有时缓存它失败是一个更好的答案。

问题#4:cache.asMap() 是否包含已过期但由于清理累积而未被驱逐的条目?

缓存将保存条目但禁止它们可见,例如通过查找或迭代。唯一的外部指示是 size(),因为在删除过期条目之前,该计数器不会更新。

问题#5:当我在 expireAfterRead() 方法中记录 currentDuration 时,它似乎与经过的时间不一致。即如果它被设置为 600000000000(10 分钟)并更新为 false 我期望在 5 分钟后它应该是 300000000000 但它不是。为什么会这样?

请用单元测试打开一个问题,我们可以诊断。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...