Spring缓存-仅当API响应成功时才清除缓存

Spring缓存-仅当API响应成功时才清除缓存,spring,spring-boot,caching,Spring,Spring Boot,Caching,我正在使用SpringCache@cacheexecute&@Cacheable 目前,我每小时运行一次调度程序以清除缓存,下次调用fetchUser()时,它将从外部APi获取数据并添加到缓存中 @Scheduled(cron = "0 0 * * * *}") @CacheEvict(value = "some-unique-value", allEntries = true) public void clearUserCache() { lo

我正在使用SpringCache
@cacheexecute
&
@Cacheable

目前,我每小时运行一次调度程序以清除缓存,下次调用fetchUser()时,它将从外部APi获取数据并添加到缓存中

@Scheduled(cron = "0 0 * * * *}")
@CacheEvict(value = "some-unique-value", allEntries = true)
public void clearUserCache() {
    log.info("Cache cleared");
}

@Cacheable(value = "some-unique-value", unless = "#result.isFailure()")
@Override
public Result<UserResponse> fetchUser() {
    try {
        UserResponse userResponse = api.fetchUserDetail();
        return Result.success(userResponse);
    } catch (Exception e) {
        return Result.failure(INTERNAL_SERVER_ERROR);
    }
}
@Scheduled(cron=“0****}”)
@cacheexecute(value=“某些唯一值”,allEntries=true)
public void clearUserCache(){
log.info(“缓存已清除”);
}
@可缓存(value=“某些唯一值”,除非=“#result.isFailure()”)
@凌驾
公共结果fetchUser(){
试一试{
UserResponse UserResponse=api.fetchUserDetail();
返回Result.success(userResponse);
}捕获(例外e){
返回结果。失败(内部服务器错误);
}
}
现在我们需要的是仅当用户API调用成功时才清除缓存。有没有办法做到这一点


现在,缓存已按计划清除,假设外部API调用失败。主API将返回错误响应。在这种情况下,我应该能够使用现有的缓存本身。

如果我得到了正确的缓存,为什么不在检查此方法的父级的API调用是否正确后,将其作为普通方法调用呢

用你的代码,一些类似于

//如果您需要,我们只需在这里安排时间。
@已计划(cron=“0****}”)
@cacheexecute(value=“某些唯一值”,allEntries=true)
public void clearUserCache(){
log.info(“缓存已清除”);
}
@可缓存(value=“某些唯一值”,除非=“#result.isFailure()”)
@凌驾
公共结果fetchUser(){
试一试{
UserResponse UserResponse=api.fetchUserDetail();
返回Result.success(userResponse);
}捕获(例外e){
返回结果。失败(内部服务器错误);
}
}
公共方法(){
Result userResult=this.fetchUser();
if(userResult.isFailure()){
这个.clearUserCache();
}
}
这样,如果抛出任何
异常
,它将返回一个
失败
状态,您可以检查它。因此,缓存将每小时被清除一次,或者在缓存不工作时被清除一次


因此,下一次,由于失败且没有缓存,它将重试。

我没有找到任何直接的实现,但通过改进,我能够做到这一点

用例

  • 只有在触发下一个使用用户API的服务调用时,才应更新用户API响应。调度程序不应更新它。因为我们需要将来自外部系统的头信息传递给用户API
  • 只有当用户API响应成功时,才能清除缓存
步骤:

  • 在调度程序中添加了一个变量,并在缓存更新时在调度时间和关闭时间将其打开
  • 此标志在
    UserService
    类中用于检查是否触发了调度程序
  • 如果没有,请使用缓存。如果
    true
    ,则触发用户API调用。如果成功,请检查响应。触发cacheexecute方法并更新缓存
示例代码:

调度配置

用户服务

用户缓存

@Cacheable(value=“USER_CACHE”,key=“#root.targetClass”,除非=“#result.isFailure()”)
公共结果getUserDetail(UserResponse UserResponse){
试一试{
如果(userResponse==null){//在缓存不可用时处理第一次触发器
userResponse=fetchUserDetail();//实际的API调用
}
返回Result.success(mapToUser(userResponse));
}捕获(例外e){
返回结果。失败(“错误响应”);
}
}
注意:

  • 结果是一个自定义包装器,假设它是一个具有成功或失败属性的对象
  • 我不得不添加
    @Cacheable
    部分作为单独的Bean,因为缓存只在代理对象上工作。如果我将
    getUserDetail
    保存在
    UserService
    中并直接调用,则它不会被拦截,因为代理和缓存逻辑不工作,每次都会触发API调用
  • 最重要的是:这不是最好的解决方案,还有改进的余地

退出缓存时,您没有进行任何API调用来明确检查API调用成功或失败。你将如何捕捉它的状态?API调用方法使用的是
@Cacheable
,这意味着,它将从缓存中读取数据,而不会进行实际的Rest调用。我说的对吗?调度程序不是用户API调用的触发点。它将使用userAPI从服务中调用,并且只有当该服务由外部系统触发时才调用。只是有一点偏差。唯一的问题是,如果缓存已经就位,您将永远无法访问“this.clearUserCache();”。Spring将返回代理对象,而不是调用实际方法。此外,由于数据需要通过任何后续API调用每小时刷新post,因此需要调度程序。我修复了我的答案。父级将调用
this.fetchUser()
,如果上一次迭代不起作用,或者已经过了一个小时,它将再次调用该方法。或者您需要对照当前迭代检查它吗?2偏差1。如果在每小时清除一次缓存,并且UserAPI关闭,则整个响应将失败。这违背了目的。2.parentMethod()不应该是空的,检查是(!userResult.isFailure()){我想你漏掉了“不是”。但总的来说,它与我实现的一致。很快就会发布。谢谢。
private boolean updateUserCache;

@Scheduled(cron = "${0 0 * * * *}") // runs every Hr
public void userScheduler() {
    updateUserCache = true;
    log.info("Scheduler triggered for User");
}

@CacheEvict(value = "USER_CACHE", allEntries = true)
public void clearUserCache() {
    updateUserCache = false;
    log.info("User cache cleared");
}

public boolean isUserCacheUpdateRequired() {
    return updateUserCache;
}
UserResponse userResponse = null;
if (schedulerConfig.isUserCacheUpdateRequired()) {
    userResponse = userCache.fetchUserDetail(); 
    if (userResponse != null) {
        // clear's cache and userResponse is stored in cache automatically when getUserDetail is called below
        schedulerConfig.clearUserCache(); 
    }
}
return userCache.getUserDetail(userResponse);
@Cacheable(value = "USER_CACHE", key = "#root.targetClass", unless = "#result.isFailure()")
public Result<User> getUserDetail(UserResponse userResponse) {
    try {
        if (userResponse == null) { // handle first time trigger when cache is not available
            userResponse = fetchUserDetail(); // actual API call 
        }
        return Result.success(mapToUser(userResponse));
    } catch (Exception e) {
        return Result.failure("Error Response");
    }
}