java - @CacheEvict 的 Gemfire EntryNotFoundException

标签 java spring caching gemfire spring-data-gemfire

简而言之,当在方法上调用 @CacheEvict 时,如果未找到条目的键,Gemfire 将抛出 EntryNotFoundException。

现在详细说明,

我有课

class Person {

 String mobile;
 int dept;
 String name;

}

我有两个缓存区域定义为 personRegion 和 personByDeptRegion,服务如下

@Service
class PersonServiceImpl {

   @Cacheable(value = "personRegion")
   public Person findByMobile(String mobile) {

      return personRepository.findByMobile(mobile);

   }


   @Cacheable(value = "personByDeptRegion")
   public List<Person> findByDept(int deptCode) {

      return personRepository.findByDept(deptCode);

   }


   @Caching(
      evict = { @CacheEvict(value = "personByDeptRegion", key="#p0.dept"},
      put = { @CachePut(value = "personRegion",key = "#p0.mobile")}

   )
   public Person updatePerson(Person p1) {

      return personRepository.save(p1);

   }

}

当调用 updatePerson 时,如果 personByDeptRegion 中没有条目,则会抛出键 1 (或任何部门代码)的 EntryNotFoundException 异常。这个方法很有可能会在 @Cacheable 方法被调用之前被调用,并且希望避免这个异常。 当给定区域的 key 不存在时,我们是否可以调整 Gemfire 的行为以优雅地返回? 另外,我也很想知道是否有使用 Gemfire 作为缓存更好地实现上述场景。

Spring 数据 Gemfire:1.7.4

Gemfire 版本:v8.2.1

注意:上面的代码仅供引用,我在实际项目中有多个服务存在相同的问题。

最佳答案

首先,我赞扬您在应用程序上使用 Spring 的缓存注释 @Service成分。开发人员经常在他们的存储库中启用缓存,我认为这是不好的形式,特别是如果在存储库交互之前或之后涉及复杂的业务规则(甚至额外的 IO;例如从服务组件调用 Web 服务) ,特别是在缓存行为不应受到影响(或确定)的情况下。

我还认为您通过遵循 personRegion 来缓存 UC(在数据存储更新时更新一个缓存 ( personByDeptRegion ),同时使另一个缓存 ( CachePut ) 无效)与 CacheEvict对我来说似乎很合理。不过,我想指出 @Caching 的看似预期用途注解是组合多个相同类型的缓存注解(例如多个 @CacheEvict 或多个 @CachePut ),如核心 Spring 框架 Reference Guide 中所述。 。尽管如此,没有什么可以阻止您的预期用途。

我创建了一个类似的测试类here ,仿照上面的示例来验证问题。确实是jonDoeUpdateSuccessful测试用例失败(使用 GemFire EntryNotFoundException ,如下所示),因为 Department 中没有人与 janeDoeUpdateSuccessful 不同,“R&D”在更新之前之前已缓存在“DepartmentPeople”GemFire 区域中。测试用例,这会导致在更新之前填充缓存(即使该条目没有值,这也没有影响)。

com.gemstone.gemfire.cache.EntryNotFoundException: RESEARCH_DEVELOPMENT
    at com.gemstone.gemfire.internal.cache.AbstractRegionMap.destroy(AbstractRegionMap.java:1435)

NOTE: My test uses GemFire as both a "cache provider" and a System of Record (SOR).

问题确实在于 SDG 使用 Region.destroy(key)GemfireCache.evict(key)实现而不是,也许更合适,Region.remove(key) .

GemfireCache.evict(key)已实现 Region.destroy(key)自成立以来。然而,Region.remove(key)直到 GemFire v5.0 才引入。尽管如此,我还是看不出 Region.destroy(key) 之间有明显的区别。和Region.remove(key)除了 EntryNotFoundExceptionRegion.destroy(key) 抛出。本质上,它们都会破坏本地条目(键和值),并将操作分发到集群中的其他缓存(假设使用了非 LOCAL Scope)。

所以,我已经提交了SGF-539将 SDG 更改为调用 Region.remove(key)GemfireCache.evict(key)而不是Region.destroy(key) .

至于解决方法,基本上您只能做两件事:

  1. 重构您的代码和 @CacheEvict 的使用注释,和/或...
  2. 利用condition@CacheEvict .

不幸的是condition无法使用类类型来指定,类似于 Spring Condition (除了 SpEL),但此接口(interface)用于其他目的,并且 @CacheEvict , condition属性不接受类类型。

目前,我还没有一个很好的例子来说明它是如何工作的,所以我将继续前进 SGF-539 .

您可以关注此票证以了解更多详细信息和进度。

抱歉给您带来不便。

-约翰

关于java - @CacheEvict 的 Gemfire EntryNotFoundException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39830488/

相关文章:

java - CustomRepositoryImpl 中的 Autowiring bean 为 null

java - 有什么方法可以启用或禁用 applicationContext.xml 文件中的 Spring bean 定义?

spring - 在 spring-data-rest 中使用验证器返回 http 500 而不是 400

Java 从文本文件中读取前 2 行

java - "Running total returns false value"

html - 为什么浏览器不从缓存中加载 cdn 文件?

caching - 如何强制浏览器重新加载缓存的 CSS 和 JS 文件?

css - 我如何确定并确定我的网页未被缓存?

java - 如何使用Java代码在Android中将复选框添加到 'Grid View'

spring - QueryDsl - 如何使用 maven 创建 Q 类?