3

我有一个需要缓存的作业列表(按 id)。然而,在某些情况下,拥有最新版本的作业很重要,并且需要绕过缓存(强制更新)。发生这种情况时,应将新获取的作业放入缓存中。

我是这样实现的:

@Cacheable(cacheNames = "jobs", key = "#id", condition = "!#forceRefresh", sync = true)
public Job getJob(String id, boolean forceRefresh) {
    // expensive fetch
}

期望的行为:

  • getJob("123", false)=> 返回作业 v1(如果存在,则从缓存中获取)
  • getJob("123", true)=> 返回作业 v2(更新版本,从数据库中获取)
  • getJob("123", false)=> 返回作业 v2(更新版本,从缓存中获取

实际上,最后一次调用会getJob("123", false)返回作业v1,即陈旧版本。似乎第二次调用(强制更新)不会更新缓存中的值。

我怎样才能在这里实现正确的行为?

缓存配置(使用咖啡因):

CaffeineCache jobs = new CaffeineCache("jobs", Caffeine.newBuilder()
        .expireAfterWrite(1, TimeUnit.MINUTES)
        .maximumSize(100)
        .build());
4

2 回答 2

6

如果forceRefresh为真,Spring 缓存将不会因为条件而被激活condition = "!#forceRefresh"。因此,缓存值不会被更新。

您需要明确告诉 Spring 更新缓存值,@CachePut以防万一forceRefresh

@Caching(
    cacheable = {@Cacheable(cacheNames = "jobs", key = "#id", condition = "!#forceRefresh")},
    put = {@CachePut(cacheNames = "jobs", key = "#id", condition = "#forceRefresh")}
)
public Job getJob(String id, boolean forceRefresh) {
    // expensive fetch
}
于 2019-12-30T09:26:57.440 回答
2

我之前遇到过这个问题并解决了几种方法。解决它的最简单方法是通过您正在使用的Job相同方式完成所有更新。JobService如果是这种情况,您只需执行以下操作:

    @Caching(evict = {
            @CacheEvict(value = "jobs", key = "#job.id") })
    public void updateJob( Job job ) {

这样,当Job更新它时,它将被驱逐到缓存中,并且您的下一次调用getJob将拉一个新的。

下一种方法是,如果您有一些其他进程正在更新您的数据库并且updateJob不用于更新实际源。那时,我已经实现了它,我构建了一个 Quartz Job 来按计划(即每 15 分钟)刷新/更新我的缓存条目。它看起来像这样。

    @Autowired
    CacheManager cacheManager;

    public void refreshJobs() {
        Cache cache = cacheManager.getCache( "jobs" );

        for ( Job job : getJobs() ) {
            cache.put( job.getId(), job );
        }
    }

您可能会使用该解决方案获得一些陈旧的作业,但您知道它每 5、10 或 15 分钟刷新一次。

于 2019-12-24T15:19:54.087 回答