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.query
calls: If your unit of work participates in an existing READ_WRITE
transaction, e.g. this happens because of the @Transactional
AOP problem above, there is no way to set the type to READ_ONLY
. As a default OGM will always create a READ_WRITE
transaction if no explicit type is set.
tl;dr;
There are two solutions for the problem in general:
@Transactional
methods into another class and leave the caller code in the existing one.Session
object manually by injecting the SessionFactory
and create a transaction with READ_ONLY
type. (and remove the @Transactional
annotation)(as answered in the Neo4j user slack)