refactor: Proper wrap retryable exceptions in TransactionSystemException.
This avoids an attempted rollback when the commit already failed which will lead to an illegal state exception otherwise, which will completely throw off the transaction system. Discovered through an internal card.
This commit is contained in:
@@ -25,6 +25,7 @@ import org.neo4j.driver.exceptions.ServiceUnavailableException;
|
||||
import org.neo4j.driver.exceptions.SessionExpiredException;
|
||||
import org.neo4j.driver.exceptions.TransientException;
|
||||
import org.springframework.dao.TransientDataAccessResourceException;
|
||||
import org.springframework.transaction.TransactionSystemException;
|
||||
|
||||
|
||||
/**
|
||||
@@ -52,13 +53,17 @@ public final class RetryExceptionPredicate implements Predicate<Throwable> {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (throwable instanceof TransactionSystemException && throwable.getCause() != null) {
|
||||
throwable = throwable.getCause();
|
||||
}
|
||||
|
||||
if (throwable instanceof IllegalStateException) {
|
||||
String msg = throwable.getMessage();
|
||||
return msg != null && RETRYABLE_ILLEGAL_STATE_MESSAGES.contains(msg);
|
||||
}
|
||||
|
||||
Throwable ex = throwable;
|
||||
if (throwable instanceof TransientDataAccessResourceException) {
|
||||
if (throwable instanceof TransientDataAccessResourceException || throwable instanceof TransactionSystemException) {
|
||||
ex = throwable.getCause();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import org.neo4j.driver.Driver;
|
||||
import org.neo4j.driver.Session;
|
||||
import org.neo4j.driver.Transaction;
|
||||
import org.neo4j.driver.TransactionConfig;
|
||||
import org.neo4j.driver.exceptions.Neo4jException;
|
||||
import org.neo4j.driver.exceptions.RetryableException;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
@@ -338,10 +340,17 @@ public final class Neo4jTransactionManager extends AbstractPlatformTransactionMa
|
||||
@Override
|
||||
protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
|
||||
|
||||
Neo4jTransactionObject transactionObject = extractNeo4jTransaction(status);
|
||||
Neo4jTransactionHolder transactionHolder = transactionObject.getRequiredResourceHolder();
|
||||
Collection<Bookmark> newBookmarks = transactionHolder.commit();
|
||||
this.bookmarkManager.resolve().updateBookmarks(transactionHolder.getBookmarks(), newBookmarks);
|
||||
try {
|
||||
Neo4jTransactionObject transactionObject = extractNeo4jTransaction(status);
|
||||
Neo4jTransactionHolder transactionHolder = transactionObject.getRequiredResourceHolder();
|
||||
Collection<Bookmark> newBookmarks = transactionHolder.commit();
|
||||
this.bookmarkManager.resolve().updateBookmarks(transactionHolder.getBookmarks(), newBookmarks);
|
||||
} catch (Neo4jException ex) {
|
||||
if (ex instanceof RetryableException) {
|
||||
throw new TransactionSystemException(ex.getMessage(), ex);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,7 @@ import reactor.util.function.Tuples;
|
||||
import org.apiguardian.api.API;
|
||||
import org.neo4j.driver.Driver;
|
||||
import org.neo4j.driver.TransactionConfig;
|
||||
import org.neo4j.driver.exceptions.RetryableException;
|
||||
import org.neo4j.driver.reactivestreams.ReactiveSession;
|
||||
import org.neo4j.driver.reactivestreams.ReactiveTransaction;
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -35,6 +36,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.NoTransactionException;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionSystemException;
|
||||
import org.springframework.transaction.reactive.AbstractReactiveTransactionManager;
|
||||
import org.springframework.transaction.reactive.GenericReactiveTransaction;
|
||||
import org.springframework.transaction.reactive.TransactionSynchronizationManager;
|
||||
@@ -326,6 +328,7 @@ public final class ReactiveNeo4jTransactionManager extends AbstractReactiveTrans
|
||||
.getRequiredResourceHolder();
|
||||
return holder.commit()
|
||||
.doOnNext(bookmark -> bookmarkManager.resolve().updateBookmarks(holder.getBookmarks(), bookmark))
|
||||
.onErrorMap(e -> e instanceof RetryableException, e -> new TransactionSystemException(e.getMessage(), e))
|
||||
.then();
|
||||
}
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@ class QuerydslNeo4jPredicateExecutorIT {
|
||||
Predicate predicate = Expressions.predicate(Ops.EQ, firstNamePath, Expressions.asString("Helge"))
|
||||
.or(Expressions.predicate(Ops.EQ, lastNamePath, Expressions.asString("B.")));
|
||||
List<Person> people = repository.findBy(predicate,
|
||||
q -> q.limit(1)).all();
|
||||
q -> q.sortBy(Sort.by("firstName").descending()).limit(1)).all();
|
||||
|
||||
assertThat(people).hasSize(1);
|
||||
assertThat(people).extracting(Person::getFirstName).containsExactly("Helge");
|
||||
|
||||
Reference in New Issue
Block a user