如何在caffeine expiry中设置多个过期标准?

0kjbasz6  于 2021-06-08  发布在  Redis
关注(0)|答案(1)|浏览(2611)

我使用的是caffeine v2.8.5,我想创建一个具有基于以下条件的可变过期时间的缓存:
创建/更新价值和
此值的最后一次访问(读取)。
不管先到的是什么,都会触发删除该条目。
缓存将是值的三层解析的一部分:
密钥存在于咖啡因缓存中
使用此值
刷新访问/读取过期
密钥存在于redis数据库中
使用此值
将此值与redis键的剩余ttl(生存时间)一起存储在缓存中
密钥既不在内部缓存中,也不在redis中
从外部RESTAPI请求值
将此值存储在redis数据库中,固定过期时间为30天
将此值存储在咖啡因缓存中,固定过期时间为30天
redis被用作全局缓存,因此多个应用程序/示例可以共享缓存的数据,但是这种解析经常发生,不能用于每个请求,因此需要另一个缓存层。
根据请求的时间,请求的数据具有不同的ttl。因此,当我们请求restapi并且在redis中设置到期时间时,到期时间可能是固定的,但是在caffeine中,到期时间将是动态的,因为到期时间是基于redis密钥的剩余ttl。
案例(2)和(3)已经在我的cacheloader中为caffeine缓存解决了(我在read-through模式下使用缓存)。为了控制过期时间,我已经发现,我必须使用高级过期api,并且我还研究了类似的问题,如(为条目指定过期时间)和(在创建时间之后过期缓存值)。所以我为我的密钥设计了一个 Package 器对象,如下所示:

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;
}

到期日是这样的:

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,
        @NotNull final ValueType value,
        final long currentTime,
        final long currentDuration
    ) {
        return currentDuration;
    }

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

在我的用例中不同的是,我希望有一个基于值的最后一次访问的第二个过期标准。所以,如果一个小时内还没有收到请求,我想早点删除条目。如果它经常被访问,它最终会在ttl为零时被删除。
我将如何实现第二个标准?我不知道如何才能得到最后一次访问条目的时间。接口似乎没有提供这样的值。我也研究了这个问题。基于已排序到的调度程序bucket,将定期调用/重新评估方法是否正确?

lzfw57am

lzfw57am1#

我对expiries如何工作的最大误解是,我认为expiries的方法会定期触发和重新评估。我在回答我自己的问题,以防有人从他们的研究中得到同样的印象。
只有在执行了相应方法名的操作之后,才会调用过期期内的方法(因此值才会更新)。比如说 expireAfterRead(K, V, long, long) 仅在每次读取缓存中的该键值Map时调用。
因此,如果在Map创建之后再也没有任何操作(没有读取或更新),那么只有 expireAfterCreate(K, V, long) 方法将被调用一次。这就是为什么所有方法都应该返回剩余的持续时间,但不必考虑最后一次读取条目的时间,例如,该时刻就是现在(如 Instant.now() ),的 expireAfterRead(K, V, long, long) 被称为。
正如@benmanes在评论中指出的那样,我最初的问题的正确解决方案正在回归

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

在所有三种方法中的有效期。
回答我在帖子里的另外两个问题:
如何获取上次访问条目的时间?呼叫(例如) Instant.now()expireAfterRead(K, V, long, long) 方法。如果还希望在外部或其他expire方法中使用该值,则始终可以使用volatile字段将该值存储在expireingvalue中。
基于已排序到的调度程序bucket,将定期调用/重新评估方法是否正确?否。如上所述,只有在执行相应的操作之后,才会调用过期内的方法。这些方法不会定期触发或重新评估。

相关问题