0

我有一个 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, b, c, 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 个正在运行的实例,就不会发生这种情况。除了生产中运行的多个实例之外,您还有其他原因吗?

4

1 回答 1

1

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

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

例如,一旦保存的对象在事务日志中,save 方法就可能返回,但尚未完全处理。或者保存在主节点上处理,但 findByHash 在辅助节点上执行,它还没有被复制。

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

更好的是让 MongoDB 使用findAndModifywith进行锁定FindAndModifyOptions.upsert(true),而忘记 Java 代码中的锁定。

于 2020-08-16T13:14:34.603 回答