Release R2DBC connection when cleanup fails in transaction
When using R2dbcTransactionManager, connection will not be released if it encounters error while doing `afterCleanup` steps. As `afterCleanup` can use a database connection when doing `setAutoCommit(true)`, it can fail under some conditions where the connection is not reliable. This leads to the Connection not being released. This commit ensures that inner steps of the `doCleanupAfterCompletion` are protected against errors, logging the errors and continuing the cleanup until the last step, which releases the connection. Closes gh-29703 Co-authored-by: Simon Baslé <sbasle@vmware.com>
This commit is contained in:
@@ -357,26 +357,41 @@ public class R2dbcTransactionManager extends AbstractReactiveTransactionManager
|
||||
Mono<Void> afterCleanup = Mono.empty();
|
||||
|
||||
if (txObject.isMustRestoreAutoCommit()) {
|
||||
afterCleanup = afterCleanup.then(Mono.from(con.setAutoCommit(true)));
|
||||
Mono<Void> restoreAutoCommitStep = safeCleanupStep(
|
||||
"doCleanupAfterCompletion when restoring autocommit", Mono.from(con.setAutoCommit(true)));
|
||||
afterCleanup = afterCleanup.then(restoreAutoCommitStep);
|
||||
}
|
||||
|
||||
return afterCleanup.then(Mono.defer(() -> {
|
||||
Mono<Void> releaseConnectionStep = Mono.defer(() -> {
|
||||
try {
|
||||
if (txObject.isNewConnectionHolder()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Releasing R2DBC Connection [" + con + "] after transaction");
|
||||
}
|
||||
return ConnectionFactoryUtils.releaseConnection(con, obtainConnectionFactory());
|
||||
return safeCleanupStep("doCleanupAfterCompletion when releasing R2DBC Connection",
|
||||
ConnectionFactoryUtils.releaseConnection(con, obtainConnectionFactory()));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
txObject.getConnectionHolder().clear();
|
||||
}
|
||||
return Mono.empty();
|
||||
}));
|
||||
});
|
||||
return afterCleanup.then(releaseConnectionStep);
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<Void> safeCleanupStep(String stepDescription, Mono<Void> stepMono) {
|
||||
if (!logger.isDebugEnabled()) {
|
||||
return stepMono.onErrorComplete();
|
||||
}
|
||||
else {
|
||||
return stepMono.doOnError(e ->
|
||||
logger.debug(String.format("Error ignored during %s: %s", stepDescription, e)))
|
||||
.onErrorComplete();
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<Void> switchAutoCommitIfNecessary(Connection con, Object transaction) {
|
||||
ConnectionFactoryTransactionObject txObject = (ConnectionFactoryTransactionObject) transaction;
|
||||
Mono<Void> prepare = Mono.empty();
|
||||
|
||||
Reference in New Issue
Block a user