在反应式 (webflux) 应用程序中正确使用可变 mongodb 实体

Correct use of mutable mongodb entities in reactive (webflux) application

当前 spring-data-mongodb 项目使用可变实体从数据库加载状态,即使在反应性应用程序中也是如此。这被认为是一种不好的做法,我什至可以在 spring-data-mongodb 项目本身中发现一些问题。例如来自 ReactiveMongoTemplate 的代码片段:

protected <T> Mono<T> doSave(String collectionName, T objectToSave, MongoWriter<Object> writer) {

    assertUpdateableIdIfNotSet(objectToSave);

    return createMono(collectionName, collection -> {

        T toSave = maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName)).getSource();

        AdaptibleEntity<T> entity = operations.forEntity(toSave, mongoConverter.getConversionService());
        Document dbDoc = entity.toMappedDocument(writer).getDocument();
        maybeEmitEvent(new BeforeSaveEvent<T>(toSave, dbDoc, collectionName));

        return saveDocument(collectionName, dbDoc, toSave.getClass()).map(id -> {

            T saved = entity.populateIdIfNecessary(id);
            return maybeEmitEvent(new AfterSaveEvent<>(saved, dbDoc, collectionName)).getSource();
        });
    });
}

如您所见,entity.populateIdIfNecessary(id) 正在另一个线程上改变实体对象,这被认为是多线程应用程序中的错误,除非您使用所有 setter 同步的实体。

使用具有反应性 mongodb 接口的可变实体来处理并发问题的正确和推荐做法是什么? 考虑这个例子:

reactiveMongoOperations.findById("id2", Customer.class) // calls Customer setters on thread1
  .zipWith(reactiveMongoOperations.findById("id2", Account.class)) // calls Account setters on thread2
  .map(t -> perform(t.getT1(), t.getT2())); // access objects on thread2

为了使此代码安全,您需要具有不可变的客户和帐户 类(spring 数据 mongodb 不支持),或者所有 setter 必须是synchronized/volatile 使您的代码非常线程敏感且容易出错。

我刚刚在有关项目反应堆和内存模型的答案中发现了类似的问题 Project Reactor and the Java memory model。因此,如果您不使用并行运算符,似乎可以使用内存屏障(volatile 关键字)来保证内存一致性。