С MongoDB в кластере сегментированных данных вы можете увидеть такого рода исключение, вы можете решить это, просто повторив транзакцию, MongoDB предоставит API обратного вызова или вы можете иметь собственную реализацию повтора.
Если вы используете Spring-data и mongoTemplate из Spring и хотите использовать withTransaction API, то API обратного вызова не будет работать, потому что он повторяет попытку для класса исключения « MongoException » Фрагмент из ClientSessionImpl class.
public <T> T withTransaction(final TransactionBody<T> transactionBody, final TransactionOptions options) {
notNull("transactionBody", transactionBody);
long startTime = ClientSessionClock.INSTANCE.now();
outer:
while (true) {
T retVal;
try {
startTransaction(options);
retVal = transactionBody.execute();
} catch (RuntimeException e) {
if (transactionState == TransactionState.IN) {
abortTransaction();
}
if (e instanceof MongoException) {
if (((MongoException) e).hasErrorLabel(TRANSIENT_TRANSACTION_ERROR_LABEL)
&& ClientSessionClock.INSTANCE.now() - startTime < MAX_RETRY_TIME_LIMIT_MS) {
continue;
}
}
throw e;
}
if (transactionState == TransactionState.IN) {
while (true) {
try {
commitTransaction();
break;
} catch (MongoException e) {
unpinServerAddressOnError(e);
if (ClientSessionClock.INSTANCE.now() - startTime < MAX_RETRY_TIME_LIMIT_MS) {
applyMajorityWriteConcernToTransactionOptions();
if (!(e instanceof MongoExecutionTimeoutException)
&& e.hasErrorLabel(UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)) {
continue;
} else if (e.hasErrorLabel(TRANSIENT_TRANSACTION_ERROR_LABEL)) {
continue outer;
}
}
throw e;
}
}
}
return retVal;
}
}
Поскольку mongoTemplate с Spring-data и с аннотацией Transactional оборачивает исключение, связанное с транзакцией, в « MongoTransactionException », следовательно, withTransaction api с mongoTemplate не выполняет повторных попыток, потому что он ищет класс « MongoException ».
Итак, если вы хотите повторить попытку, используя весенние данные с mongoTemplate, вы можете используйте Retryable framework, как показано ниже.
@Transactional(value = "mongoTransactionManager", propagation = Propagation.REQUIRED)
@Retryable(value = {MongoCommandException.class, MongoException.class}, exclude = {MongoTransactionException.class, UncategorizedMongoDbException.class},
backoff = @Backoff(delay = 10), maxAttempts = 10)
public void performingTransactionaOperations(DocumentBean1 document1, DocumentBean2 document2, DocumentBean3 document3){
try {
mongoTemplate.insert(document1);
mongoTemplate.insert(document2);
mongoTemplate.insert(document3);
}catch (MongoTransactionException | UncategorizedMongoDbException ex){
MongoException mongoException = null;
if(ex.getCause() instanceof MongoException) {
mongoException = (MongoException) ex.getCause();
}else if(ex.getCause() instanceof MongoCommandException){
mongoException = (MongoCommandException) ex.getCause();
}
if(mongoException!=null && mongoException.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL)) {
System.out.println("TransientTransactionError aborting transaction and retrying ...");
throw mongoException;
}else if(mongoException!=null && mongoException.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)){
log.debug("UnknownTransactionCommitResult, retrying commit operation ...");
throw mongoException;
}
throw ex;
}
}