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.
+ * If a commit operation encounters an error, the MongoDB driver throws a {@link MongoException} holding + * {@literal error labels}.
+ * By default those labels are ignored, nevertheless one might check for + * {@link MongoException#UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL transient commit errors labels} and retry the the + * commit.
+ * + *

+	 * 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.