Spring Boot控制器可防止在mongodb

问题描述

我有一个REST API,可以根据请求计算内容,如果再次发出相同的请求,请从缓存中返回结果,该缓存由保存在MongoDB中的文档组成。要知道两个请求是否相同,我对请求中的一些相关字段进行了哈希处理。但是当快速连续提出相同的请求时,MongoDB中会出现重复的文档,当我尝试读取它们时,稍后会导致“ IncorrectResultSizeDataAccessException”。

解决这个问题,我尝试在以下控制器方法中同步哈希值(试图切掉不相关的部分):

@PostMapping(
        path = "/{myPath}",consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE},produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
@Async("asyncExecutor")
public CompletableFuture<ResponseEntity<?>> retrieveAndCache( ... a,b,c,d varIoUs request parameters) {
    
    //perform some validations on request...
    
    //hash relevant equest parameters
    
    int hash = Objects.hash(a,d);
    
    synchronized (Integer.toString(hash).intern()) {

        Optional<Result> resultOpt = cacheService.findByHash(hash);
        
        if (resultOpt.isPresent()) {
            return CompletableFuture.completedFuture(ResponseEntity.status(HttpStatus.OK).body(opt.get().getResult()));
        } else {
            Result result = ...//perform requests to external services and do some calculations...
            cacheService.save(result);
            
            return CompletableFuture.completedFuture(ResponseEntity.status(HttpStatus.OK).body(result));
        }
        
    }
}



//cacheService methods
@Transactional
public Optional<Result> findByHash(int hash) {
    return repository.findByHash(hash); //this is the part that throws the error
}

我确定不会发生哈希冲突,只是在快速连续执行相同请求时才发生重复记录。据我了解,只要我的Spring Boot应用程序只有1个正在运行的实例,就不应发生这种情况。除了生产环境中正在运行多个实例之外,您是否还看到其他原因?

解决方法

您应检查MongoDB客户端的设置。

如果一个线程调用cacheService.save(result)方法,并在该方法返回后释放锁,然后另一个线程调用cacheService.findByHash(hash),则仍然有可能找不到您刚刚保存的记录。

例如只要已保存的对象在事务日志中但尚未完全处理,save方法就会返回。或在主节点上处理保存,但是findByHash在尚未复制的辅助节点上执行。

您可以使用WriteConcern.MAJORITY,但我不确定100%是否涵盖所有内容。

更好的是让MongoDB通过将findAndModifyFindAndModifyOptions.upsert(true)一起使用来进行锁定,而忘记了Java代码中的锁定。