咖啡因有效期内如何设置多个有效标准?

问题描述

我正在使用caffeine v2.8.5,并且我想基于以下内容创建一个变量到期的缓存:

  • 值的创建/更新和
  • 对该值的最后访问(读取)。

首先出现的内容应触发该条目的删除


缓存将是三层值解析的一部分:

  1. 密钥存在于咖啡因缓存中
    • 使用此值
    • 刷新访问权限/读取过期
  2. 密钥存在于Redis数据库
  3. 内部缓存或Redis中都不存在密钥
    • 从外部REST API请求值
    • 将此值存储在Redis数据库中,有效期为30天
    • 将此值存储在caffeine缓存中,有效期为30天

Redis用作全局缓存,因此多个应用程序/实例可以共享缓存的数据,但是这种解决方法经常发生,以致不能用于每个请求,因此需要另一个缓存层。 / em>

根据请求时间,请求的数据具有不同的TTL。因此,尽管当我们请求REST API时到期时间可能是固定的,并且在Redis中设置了到期时间,但是在caffeine中该时间将是动态的,因为到期时间基于Redis Key的剩余TTL。

caffeine缓存的CacheLoader中已经解决了情况(2)和(3)(我在直通模式下使用了缓存)。为了控制我已经发现的到期时间,我将不得不使用advanced Expiry API,并且还研究了类似的问题,例如(Specify expiry for an Entry)(Expire cached values after creation time)。所以我想出了一个包装对象,像这样:

import lombok.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.time.Instant;

@Value
public class ExpiringValue<ValueType> {

    @Nullable
    private final ValueType value;
    @NotNull
    private final Instant validUntil;
}

和这样的Expiry

import com.github.benmanes.caffeine.cache.Expiry;
import org.jetbrains.annotations.NotNull;

import java.time.Duration;
import java.time.Instant;

public final class ValueBasedExpiry<KeyType,ValueType extends ExpiringValue<?>> implements Expiry<KeyType,ValueType> {

    @Override
    public long expireAfterCreate(
        @NotNull final KeyType key,@NotNull final ValueType value,final long currentTime
    ) {
        return Duration.between(Instant.Now(),value.getValidUntil()).toNanos();
    }

    @Override
    public long expireAfterUpdate(
        @NotNull final KeyType key,final long currentTime,final long currentDuration
    ) {
        return currentDuration;
    }

    @Override
    public long expireAfterRead(
        @NotNull final KeyType key,final long currentDuration
    ) {
        return currentDuration;
    }
}

用例的不同之处在于,我想根据值的最后一次访问获得第二个到期条件。因此,如果一个小时都没有请求,我想尽早删除该条目。而且,如果它经常访问,它将在TTL达到零后最终被删除

我将如何实施第二个条件?我不知道上次访问条目的方式。接口似乎没有提供这样的值。我还研究了this question。是否会根据调度程序存储段定期对方法进行调用/重新评估,是否正确?

解决方法

我对Expiries的工作方式有一个很大的误解,就是我认为,到期方法会定期触发并重新评估。我正在回答我自己的问题,以防有人可能从他们的研究中获得相同的印象。

仅在执行了相应方法名称的操作后,才会调用Expiry中的方法(因此仅更新值)。因此,例如,expireAfterRead(K,V,long,long)仅在每次在缓存中读取此键值映射时才会调用。

因此,如果在创建映射后将不再有任何动作(无读取或更新),则仅会调用expireAfterCreate(K,long)方法一次。这就是为什么所有方法都应始终返回剩余持续时间的原因,而不必考虑例如上一次读取条目的最后一次时间,因为那一刻就存在了(如Instant.now() ),则调用expireAfterRead(K,long)

正如@BenManes在评论中指出的那样,我最初的问题的正确解决方案正在返回

Math.min(TimeUnit.HOURS.toNanos(1),Duration.between(Instant.now(),value.getValidUntil()).toNanos())

所有三种到期日方法。


并在帖子中回答我的其他两个问题:

如何获取上次访问条目的时间?Instant.now()方法中调用(例如)expireAfterRead(K,long)。如果您还想在外部或其他expire-method中使用该值,则始终可以选择将此值存储在带有易变字段的ExpiringValue中。

是否正确,将根据调度程序存储桶定期对这些方法进行调用/重新评估,以确认条目已被分类? 否。如上所述,仅当执行了相应的操作后,才会调用Expiry中的方法。这些方法不会定期触发或重新评估。

相关问答

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