【问题标题】:Caffeine LoadingCache - Eviction with Custom expiration policyCaffeine LoadingCache - 具有自定义过期策略的驱逐
【发布时间】:2021-06-02 18:05:24
【问题描述】:

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

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

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

  private RemovalListener<String, Boolean> removeListener =
      (key, value, cause) ->
          logger.info(
              "Entry for Key={} with value={} was removed ({}) from cache", key, value, 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, Boolean value, 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, Boolean value, long currentTime, 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:如果CacheLoader 抛出RuntimeExceptionkeyExistsOnServer(key) 方法调用cache.get(key) 时会发生什么?

问题#4cache.asMap() 是否包含已过期但未因应计清理而被驱逐的条目?

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

【问题讨论】:

  • @Louis Wasserman 你能帮忙吗?

标签: java caching guava caffeine-cache


【解决方案1】:

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

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

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

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

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

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

问题#3:如果调用cache.get(key)的keyExistsOnServer(key)方法CacheLoader抛出RuntimeException怎么办?

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

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

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

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

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

【讨论】:

  • 感谢 Ben,对于 Q#2,cache.get(key) 是否会调用 CacheLoader,以获取已过期但未被驱逐的条目?文档说It will never be visible to read or write operations,请您确认一下。对 Q#4 和 Q#5 有什么想法吗?
  • 已更新,希望对您有所帮助。
  • 非常感谢 Ben,这真的很有帮助。不过,我确实看到了一些奇怪的行为,对于一个键,如果我调用 get(k),那么键/值应该在那之后被缓存,我也看到了来自 expireAfterCreate() 方法的日志。现在,我希望从缓存中返回对该键的后续调用。但是,第二个get(k) 调用也调用CacheLoader 来获取值。第三个get(k) 按预期工作并从缓存本身获取值。想知道为什么会出现这种行为,缓存条目的创建是否也会累积?然后再执行?
  • 没关系,我有 2 个应用程序实例正在运行。因此,第二次呼叫可能已经转到可能是这种情况的另一个实例。我会检查的。谢谢
猜你喜欢
  • 1970-01-01
  • 2013-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-10
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多