diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java index 5ec4e10e7..f884572d4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java @@ -47,13 +47,18 @@ import com.mongodb.client.ClientSession; * Application code is required to retrieve the {@link com.mongodb.client.MongoDatabase} via * {@link MongoDatabaseUtils#getDatabase(MongoDbFactory)} instead of a standard {@link MongoDbFactory#getDb()} call. * Spring classes such as {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly. + *
+ * By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. One may override + * {@link #doCommit(MongoTransactionObject)} to implement the + * Retry Commit Operation + * behaviour as outlined in the MongoDB reference manual. * * @author Christoph Strobl * @author Mark Paluch * @currentRead Shadow's Edge - Brent Weeks * @since 2.1 * @see MongoDB Transaction Documentation - * @see MongoDatabaseUtils#getDatabase(MongoDbFactory, SessionSynchronization) + * @see MongoDatabaseUtils#getDatabase(MongoDbFactory, SessionSynchronization) */ public class MongoTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean { @@ -181,7 +186,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager * org.springframework.transaction.support.AbstractPlatformTransactionManager#doCommit(org.springframework.transaction.support.DefaultTransactionStatus) */ @Override - protected void doCommit(DefaultTransactionStatus status) throws TransactionException { + protected final void doCommit(DefaultTransactionStatus status) throws TransactionException { MongoTransactionObject mongoTransactionObject = extractMongoTransaction(status); @@ -191,14 +196,44 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager } try { - mongoTransactionObject.commitTransaction(); - } catch (MongoException ex) { + doCommit(mongoTransactionObject); + } catch (Exception ex) { throw new TransactionSystemException(String.format("Could not commit Mongo transaction for session %s.", debugString(mongoTransactionObject.getSession())), ex); } } + /** + * Customization hook to perform an actual commit of the given transaction.
+ *
+ * int retries = 3;
+ * do {
+ * try {
+ * transactionObject.commitTransaction();
+ * break;
+ * } catch (MongoException ex) {
+ * if (!ex.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)) {
+ * throw ex;
+ * }
+ * }
+ * Thread.sleep(500);
+ * } while (--retries > 0);
+ *
+ *
+ *
+ * @param transactionObject never {@literal null}.
+ */
+ protected void doCommit(MongoTransactionObject transactionObject) throws Exception {
+ transactionObject.commitTransaction();
+ }
+
/*
* (non-Javadoc)
* org.springframework.transaction.support.AbstractPlatformTransactionManager#doRollback(org.springframework.transaction.support.DefaultTransactionStatus)
@@ -386,7 +421,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
* @since 2.1
* @see MongoResourceHolder
*/
- static class MongoTransactionObject implements SmartTransactionObject {
+ protected static class MongoTransactionObject implements SmartTransactionObject {
private @Nullable MongoResourceHolder resourceHolder;
@@ -406,7 +441,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
/**
* @return {@literal true} if a {@link MongoResourceHolder} is set.
*/
- boolean hasResourceHolder() {
+ final boolean hasResourceHolder() {
return resourceHolder != null;
}
@@ -428,14 +463,14 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
/**
* Commit the transaction.
*/
- void commitTransaction() {
+ public void commitTransaction() {
getRequiredSession().commitTransaction();
}
/**
* Rollback (abort) the transaction.
*/
- void abortTransaction() {
+ public void abortTransaction() {
getRequiredSession().abortTransaction();
}
@@ -451,7 +486,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
}
@Nullable
- ClientSession getSession() {
+ public ClientSession getSession() {
return resourceHolder != null ? resourceHolder.getSession() : null;
}
diff --git a/src/main/asciidoc/reference/client-session-transactions.adoc b/src/main/asciidoc/reference/client-session-transactions.adoc
index e27aa2d8b..887deaa1c 100644
--- a/src/main/asciidoc/reference/client-session-transactions.adoc
+++ b/src/main/asciidoc/reference/client-session-transactions.adoc
@@ -315,6 +315,14 @@ MongoDB does *not* support collection operations, such as collection creation, w
affects the on the fly collection creation that happens on first usage. Therefore make sure to have all required
structures in place.
+*Transient Errors*
+
+MongoDB can add special labels to errors raised during a transactional execution. Those may indicate transient failures
+that might vanish by simply retrying the operation.
+We highly recommend https://github.com/spring-projects/spring-retry[Spring Retry] for those purposes. Nevertheless
+one may override `MongoTransactionManager#doCommit(MongoTransactionObject)` to implement a https://docs.mongodb.com/manual/core/transactions/#retry-commit-operation[Retry Commit Operation]
+behaviour as outlined in the MongoDB reference manual.
+
*Count*
MongoDB `count` operates upon collection statistics which may not reflect the actual situation within a transaction.