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
库构建,并使用如下修复程序切换到“取消时回滚”策略,以避免此类意外情况。但这意味着完全有效的反应堆运营商(使用取消信号作为其正常功能的一部分)将在事务运营商的下游变得不可用(因为他们通常发出的取消将回滚事务)这里有一篇关于这个问题的文章:(免责声明:我是这篇文章的作者)。哪个类调用
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();
}