Spring缓存:基于条件强制更新缓存

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。似乎第二次调用(强制更新)没有更新缓存中的值。

如何才能实现正确的行为呢?

缓存配置(使用Caffeine):

CaffeineCache jobs = new CaffeineCache("jobs", Caffeine.newBuilder()
        .expireAfterWrite(1, TimeUnit.MINUTES)
        .maximumSize(100)
        .build());
2个回答

5
如果 forceRefresh 是 true,则由于条件 condition = "!#forceRefresh",Spring 缓存将不会被激活。因此,缓存值不会被更新。
forceRefresh 为 true 的情况下,您需要使用 @CachePut 明确告诉 Spring 更新缓存值:
@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
}

请注意,在同一个方法上同时使用CachePut和Cacheable注释通常是强烈不建议的。 - Madbreaks
https://docs.spring.io/spring-framework/docs/4.3.17.RELEASE/spring-framework-reference/html/cache.html#cache-annotations-put "... 除了特定的边缘情况(例如具有互相排斥条件的注释)," - Zeph

2

我之前也遇到过这个问题,有几种方法可以解决。最简单的方法是通过使用同一个JobService来完成所有对Job的更新。如果是这种情况,你只需要这样做:

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

这种方式是当Job更新时,它将被从缓存中清除,你下一次调用getJob将获取一个新的。

另一种方式是如果你有其他进程更新数据库,而updateJob未用于更新实际源。在这种情况下,我已经实现了一个Quartz作业来定期刷新/更新我的缓存条目(例如每15分钟)。它看起来像这样。

    @Autowired
    CacheManager cacheManager;

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

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

你可能会得到一些过期的任务,但你知道它每5、10或15分钟就会被刷新。


谢谢你的回答。不幸的是,更新是由我无法控制的某个进程完成的,所以第一种解决方案对我来说不可行。至于第二种解决方案:当forceRefresh为true时,我确实需要作业的最新版本,因此那也行不通。 - fikkatra
你如何知道何时调用 forceRefresh 为 true 的函数? - Chris Savory
那是客户的决定。在我的情况下,我从前端获取参数。 - fikkatra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接