Rollback reactive transaction on cancel

This commit introduces a change in reactive transaction semantics for
cancel signals. Canceling a subscription now rolls back a reactive transaction
to achieve a deterministic transaction outcome.

Previously, cancel signals committed a transaction which could
cause partially committed transactions depending on when the cancel happened.
This commit is contained in:
Mark Paluch
2020-06-16 11:00:58 +02:00
committed by Juergen Hoeller
parent 8853f4b883
commit 217b6e37a6
8 changed files with 240 additions and 35 deletions

View File

@@ -878,7 +878,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
},
this::commitTransactionAfterReturning,
(txInfo, err) -> Mono.empty(),
this::commitTransactionAfterReturning)
this::rollbackTransactionOnCancel)
.onErrorResume(ex ->
completeTransactionAfterThrowing(it, ex).then(Mono.error(ex)));
}
@@ -908,7 +908,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
},
this::commitTransactionAfterReturning,
(txInfo, ex) -> Mono.empty(),
this::commitTransactionAfterReturning)
this::rollbackTransactionOnCancel)
.onErrorResume(ex ->
completeTransactionAfterThrowing(it, ex).then(Mono.error(ex)));
}
@@ -975,6 +975,16 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
return Mono.empty();
}
private Mono<Void> rollbackTransactionOnCancel(@Nullable ReactiveTransactionInfo txInfo) {
if (txInfo != null && txInfo.getReactiveTransaction() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Rolling back transaction for [" + txInfo.getJoinpointIdentification() + "] after cancellation");
}
return txInfo.getTransactionManager().rollback(txInfo.getReactiveTransaction());
}
return Mono.empty();
}
private Mono<Void> completeTransactionAfterThrowing(@Nullable ReactiveTransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getReactiveTransaction() != null) {
if (logger.isTraceEnabled()) {

View File

@@ -79,7 +79,7 @@ final class TransactionalOperatorImpl implements TransactionalOperator {
// Need re-wrapping of ReactiveTransaction until we get hold of the exception
// through usingWhen.
return status.flatMap(it -> Mono.usingWhen(Mono.just(it), ignore -> mono,
this.transactionManager::commit, (res, err) -> Mono.empty(), this.transactionManager::commit)
this.transactionManager::commit, (res, err) -> Mono.empty(), this.transactionManager::rollback)
.onErrorResume(ex -> rollbackOnException(it, ex).then(Mono.error(ex))));
})
.subscriberContext(TransactionContextManager.getOrCreateContext())
@@ -100,7 +100,7 @@ final class TransactionalOperatorImpl implements TransactionalOperator {
action::doInTransaction,
this.transactionManager::commit,
(tx, ex) -> Mono.empty(),
this.transactionManager::commit)
this.transactionManager::rollback)
.onErrorResume(ex ->
rollbackOnException(it, ex).then(Mono.error(ex))));
})