Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring被动事务在取消生成部分提交时被提交_Java_Spring_Reactive Programming_Spring Data Mongodb_Spring Transactions - Fatal编程技术网

Java Spring被动事务在取消生成部分提交时被提交

Java Spring被动事务在取消生成部分提交时被提交,java,spring,reactive-programming,spring-data-mongodb,spring-transactions,Java,Spring,Reactive Programming,Spring Data Mongodb,Spring Transactions,我的项目使用spring数据mongodb,一切都是被动的。有一个bean具有使用声明性事务的事务方法。相关代码片段如下所示: @Configuration public class Config { @Bean public ReactiveMongoTransactionManager reactiveMongoTransactionManager() { return new ReactiveMongoTransactionManager(reactive

我的项目使用
spring数据mongodb
,一切都是被动的。有一个bean具有使用声明性事务的事务方法。相关代码片段如下所示:

@Configuration
public class Config {

    @Bean
    public ReactiveMongoTransactionManager reactiveMongoTransactionManager() {
        return new ReactiveMongoTransactionManager(reactiveMongoDbFactory());
    }

    ...
}

@Service
public class MyService {
    private final ReactiveMongoOperations mongoOperations;

    ...

    @Transactional
    public Mono<User> saveUser(User user) {
        return mongoOperations.insert(user).then(anotherInsertOnMongoOperations()).thenReturn(user);
    }
}
。。。接着,然后

DEBUG o.s.d.m.ReactiveMongoTransactionManager -  Initiating transaction commit
DEBUG o.s.d.m.ReactiveMongoTransactionManager -  About to commit transaction for session [ClientSessionImpl@62de8058 id = {"id": {"$binary": "fye2h5JkRh6yL3MTqtC0Xw==", "$type": "04"}}, causallyConsistent = true, txActive = true, txNumber = 2, error = d != java.lang.Boolean].
但有时,正如我从数据库的内容中看到的那样,只有第一个插入被持久化,第二个插入被丢失。在尝试对这种情况进行建模之后,我发现当整个反应管道被取消时,这种“损失”就会发生(不是每次,但我能够生成一个测试,以高概率再现这种情况)

我在方法的最后一个操作符之后添加了
.doonAccessorError()
.doOnCancel()
,并进行了一些日志记录。在“正常”情况下(无取消),
doonAccessorError
成功记录。但当发生取消时,有时日志中的事件顺序如下所示:

                            return Mono.<Object, ReactiveTransactionInfo>usingWhen(
                                    Mono.just(it),
                                    txInfo -> {
                                        try {
                                            return (Mono<?>) invocation.proceedWithInvocation();
                                        }
                                        catch (Throwable ex) {
                                            return Mono.error(ex);
                                        }
                                    },
                                    this::commitTransactionAfterReturning,
                                    (txInfo, err) -> Mono.empty(),
                                    this::rollbackTransactionDueToCancel)

    private Mono<Void> rollbackTransactionDueToCancel(@Nullable ReactiveTransactionInfo txInfo) {
        if (txInfo != null && txInfo.getReactiveTransaction() != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Rolling transaction back for [" + txInfo.getJoinpointIdentification() + "] due to cancel");
            }
            return txInfo.getTransactionManager().rollback(txInfo.getReactiveTransaction());
        }
        return Mono.empty();
    }
  • 事务已启动
  • 插入发生了
  • 取消发生
  • 最后的
    doonAccessorError()
    没有记录任何内容,在那里的
    onCancel()
    中记录了一些内容(因此取消似乎发生在业务方法执行的“中间”)
  • 。。。但是事务仍然被提交
  • TransactionSpectSupport.ReactiveTransactionSupport
    包含以下代码(用于本例):


    (基本上,只是将on cancel行为更改为rollback),使用此修补程序,我的测试不再产生任何不一致的数据。

    事实证明,由于意外的取消,确实有可能在中途提交被动Spring事务:

    问题是由于“取消时提交”策略造成的。Spring的伙计们计划在Spring5.3中将其切换为“取消时回滚”策略。目前的选择是:

  • 如果您的某些事务包含多个写入,请使用自定义的
    spring tx
    库构建,并使用如下修复程序切换到“取消时回滚”策略,以避免此类意外情况。但这意味着完全有效的反应堆运营商(使用取消信号作为其正常功能的一部分)将在事务运营商的下游变得不可用(因为他们通常发出的取消将回滚事务)
  • 如果您的所有事务每次最多有一次写入,那么您可以安全地使用未修补的Spring版本。但请注意,Spring公司(目前)将在5.3中改变政策

  • 这里有一篇关于这个问题的文章:(免责声明:我是这篇文章的作者)。

    哪个类调用
    saveUser
    方法?@ModusTollens我不能在这里显示调用方代码,它是一个封闭的源代码,包含太多不相关的信息。但是,最终,业务方法(它甚至被命名为不同的名称,这里所有的名称都被更改以保护无辜者)是从一个代码调用的,该代码在
    KafkaReceiver.receive()上执行
    concatMap()
    ,来自
    reactor kafka
    。由于重试而取消(使用了
    retryBackoff()
    )。但这一切似乎与问题无关,只是想确保它不是由定义它的同一类调用的(
    MyService
    )。这会导致问题。你能显示stacktrace吗?@ModusTollens问题是关于“取消时提交”。取消的原因可能不同。我甚至不确定它是否真的是由
    retryBackoff()
    行为引起的,所以我没有堆栈跟踪(这里也不相关)。
                                return Mono.<Object, ReactiveTransactionInfo>usingWhen(
                                        Mono.just(it),
                                        txInfo -> {
                                            try {
                                                return (Mono<?>) invocation.proceedWithInvocation();
                                            }
                                            catch (Throwable ex) {
                                                return Mono.error(ex);
                                            }
                                        },
                                        this::commitTransactionAfterReturning,
                                        (txInfo, err) -> Mono.empty(),
                                        this::commitTransactionAfterReturning)
    
                                return Mono.<Object, ReactiveTransactionInfo>usingWhen(
                                        Mono.just(it),
                                        txInfo -> {
                                            try {
                                                return (Mono<?>) invocation.proceedWithInvocation();
                                            }
                                            catch (Throwable ex) {
                                                return Mono.error(ex);
                                            }
                                        },
                                        this::commitTransactionAfterReturning,
                                        (txInfo, err) -> Mono.empty(),
                                        this::rollbackTransactionDueToCancel)
    
        private Mono<Void> rollbackTransactionDueToCancel(@Nullable ReactiveTransactionInfo txInfo) {
            if (txInfo != null && txInfo.getReactiveTransaction() != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Rolling transaction back for [" + txInfo.getJoinpointIdentification() + "] due to cancel");
                }
                return txInfo.getTransactionManager().rollback(txInfo.getReactiveTransaction());
            }
            return Mono.empty();
        }