I am currently working on a Spring Data Neo4j 5.0.3 REST API application that interfaces with a Neo4j 3.3.1 causal cluster consisting of 3 core nodes (1 leader and 2 followers). For better or for worse, we are also submitting a lot of custom cypher queries to the database using session.query a la SQL prepared statements. When we do this, we notice that virtually none of our custom Cypher submitted via session.query gets sent to any of the read-only followers.
I've cracked into the code and noticed that within Neo4jSession, the query method always creates a transaction with type READ_WRITE. Is there any way to get around this so that our queries get distributed across our cluster correctly?
I have also tried marking the appropriate methods with @Transactional(readOnly = true) but it doesn't seem to work. When I did into Neo4jTransactionManager, I see the following around line 218:
private Transaction.Type getTransactionType(TransactionDefinition definition, Neo4jTransactionObject txObject) {
Transaction.Type type;
if (definition.isReadOnly() && txObject.isNewSessionHolder()) {
type = Transaction.Type.READ_ONLY;
} else if (txObject.transactionData != null) {
type = txObject.transactionData.type();
} else {
type = Transaction.Type.READ_WRITE;
}
return type;
}
What does that second condition, isNewSessionHolder, in the first branch mean? Within the context of a single HTTP API call, we call out to the database at least 2 times, so by the second query, I believe this condition always returns false.
Given these observations, are there any easy ways forward for making my queries be respected as read-only?
First part regarding Spring: Due to limitations of Spring AOP, it is not possible to have multiple isolated transactions within one class. The best solution would be to separate the calling code from the transactional methods in different classes. Then the
@Transactional(readOnly = true)will work.Second part regarding OGM's
session.querycalls: If your unit of work participates in an existingREAD_WRITEtransaction, e.g. this happens because of the@TransactionalAOP problem above, there is no way to set the type toREAD_ONLY. As a default OGM will always create aREAD_WRITEtransaction if no explicit type is set.tl;dr;
There are two solutions for the problem in general:
@Transactionalmethods into another class and leave the caller code in the existing one.Sessionobject manually by injecting theSessionFactoryand create a transaction withREAD_ONLYtype. (and remove the@Transactionalannotation)(as answered in the Neo4j user slack)