具有复杂条件逐出的缓存

Cache With Complex Conditional Eviction

我正在尝试使用 caffeinespring-boot-starter-cache 来实现以下缓存逻辑:

  1. 如果到期时间已经过了条件(需要计算和I/O)被评估为 TRUE 然后强制获取数据并更新缓存。
  2. 如果到期时间已经过了条件(需要计算和I/O)被评估为 FALSE 然后不要使缓存数据无效并从缓存中检索值。
  3. 如果过期时间 已过,则从缓存中检索值。

我按照这个指南工作: https://www.baeldung.com/spring-boot-caffeine-cache

我在缓存对象的 getter 方法上尝试了各种使用 @CachePut@CacheEvict@Cacheable 的方法,但核心问题是我需要用过期时间和另一个逻辑来调节驱逐,但这些注释无法控制是否驱逐……也许这可以使用 Scheduler?

来完成

您似乎没有使用咖啡因作为缓存系统。

在这种情况下,使用自定义 class 并在简单的 Map 中保存数据会更好,因为它为您提供了更大的灵活性。

这是骨架

public class SpecialCache {
    private Map<String, SpecialCache.Entry> map = new HashMap<>();

    private boolean specialCondition(String key) {
        ...
    }

    private Object fetch(String key) {
       ...
    }


    public Object get(String key) {
        SpecialCache.Entry entry = map.get(key);
        if (entry.getExpiringEpoch() > System.currentTimeMillis()) {
            if (specialCondition(key)) {
                Object data = fetch(key);
                entry.setExpiringEpoch(...);
                entry.setData(data);
                return data;
            } else {
                return entry.getData();
            } 
        } else {
            return entry.getData();
        }
    }

    @Data
    public static class Entry {
        private long expiringEpoch;
        private Object data;
    }
}

在示例中,我在缓存中添加了 specialCondition 和 fetch 方法。您还可以将这些方法作为 lambda 函数传递给 get 方法,以获得更大的灵活性。

代码必须填写,例如您需要添加:

  • 检查缓存中不存在的键
  • 填充缓存的方法(放置?)

我认为您正在寻找 refreshAfterWrite 并覆盖 CacheLoader.reload(K, V)。 这是解释细节的post:https://github.com/ben-manes/caffeine/wiki/Refresh

您的案例的实施类似于:

@Log4j2
@Configuration
public class CacheConfig {

    @Bean
    public Cache<String,Item> caffeineConfig() {
        return Caffeine.newBuilder()
            .refreshAfterWrite(10, TimeUnit.SECONDS)
            .build(new ConditionalCacheLoader<>(this::shouldReload, this::load));
    }

    private Item load(String key){
        //load the item
        return null;
    }

    private boolean shouldReload(Item oldValue){
        //your condition logic here
        return true;
    }


    private static class Item{
         // the item value can contain any data
    }

    private static class ConditionalCacheLoader<K,V> implements CacheLoader<K,V>{
        private final Predicate<V> shouldReload;

        private final Function<K,V> load;

        protected ConditionalCacheLoader(Predicate<V> shouldReload, Function<K, V> load) {
            this.shouldReload = shouldReload;
            this.load = load;
        }

        @Override
        public V load(K key) throws Exception{
            return load.apply(key);
        }

        @Override
        public V reload(K key, V oldValue) throws Exception {
            if (shouldReload.test(oldValue)){
                return load(key);
            }else {
                return oldValue;
            }
        }
    }
}