Spring Cache:根据条件强制更新缓存

问题描述 投票:3回答:2

我有一个需要缓存的作业列表(按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());
spring spring-boot caching spring-cache caffeine
2个回答
4
投票

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

[在@CachePut为true的情况下,您需要明确告知Spring用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
}

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

您可能会通过该解决方案得到一些过时的Jobs,但是您知道它每5、10或15分钟就会刷新一次。

© www.soinside.com 2019 - 2024. All rights reserved.