Neo4j deadlock for creating relationships between nodes locked in one transaction
My Neo4j server is throwing DeadlockDetectedExceptions when doing parallel requests and I can't figure out why.
The following code is called by a server plugin called by client REST requests and happens in a single transaction.
Node nFollowing = loadUser(idFollowing);
Node nFollowed = loadUser(idFollowed);
// locking order to avoid deadlocks
if (Long.valueOf(idFollowing) < Long.valueOf(idFollowed)) {
tx.acquireWriteLock(nFollowing);
tx.acquireWriteLock(nFollowed);
} else {
tx.acquireWriteLock(nFollowed);
tx.acquireWriteLock(nFollowing);
}
// create relationship if not present
for (Relationship followship : nFollowing.getRelationships(
EdgeType.FOLLOWS, Direction.OUTGOING)) {
if (followship.getEndNode().equals(nFollowed)) {
return;
}
}
nFollowing.createRelationshipTo(nFollowed, EdgeType.FOLLOWS);
While I was thinking about deadlocks, the following exception is thrown, indicating the deadlock that occurs when the relationship is created.
Exception (slightly reduced):
"ForsetiClient[78] can't acquire ExclusiveLock{owner=ForsetiClient[73]} on
RELATIONSHIP(49), because holders of that lock are waiting for ForsetiClient[78].\n
Wait list:ExclusiveLock[ForsetiClient[73] waits for [73, 78, ]]",
"exception" : "DeadlockDetectedException",
"fullname" : "org.neo4j.kernel.DeadlockDetectedException",
"stacktrace" : [
"org.neo4j.kernel.ha.lock.forseti.ForsetiClient.markAsWaitingFor(ForsetiClient.java:611)",
"org.neo4j.kernel.ha.lock.forseti.ForsetiClient.acquireExclusive(ForsetiClient.java:190)",
"org.neo4j.kernel.impl.nioneo.xa.TransactionalRelationshipLocker.getWriteLock(TransactionalRelationshipLocker.java:33)",
"org.neo4j.kernel.impl.core.NodeProxy.createRelationshipTo(NodeProxy.java:455)",
"de.uniko.sebschlicht.neo4j.graphity.WriteOptimizedGraphity.addFollowship(WriteOptimizedGraphity.java:40)",
"de.uniko.sebschlicht.neo4j.graphity.Graphity.addFollowship(Graphity.java:115)",
"de.uniko.sebschlicht.neo4j.GraphityBaselinePlugin.follow(GraphityBaselinePlugin.java:38)",
"org.neo4j.server.rest.web.ExtensionService.invokeGraphDatabaseExtension(ExtensionService.java:134)",
"org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:139)"
]
I can't figure out why: I already have a lock for nFolloing
and nFollowed
. Indeed, deadlock is not meant to lock node, but relationship lock. Since relationships are only created when they are not already present, the lock holder must be current.
If that's right, the lock holder is waiting for himself. How can I avoid this or what else is the exception trying to say?
Just in case: I don't want to use the synchronized
blocks that are suggested and also marked how Neo4j should be avoided as it will slow down my work significantly and I don't expect these parallel queries to happen very often.
source to share
The deadlock was caused by a block snychronized
along with a manual transaction lock. I missed removing the statement synchronized
from the test I ran earlier. Although the statement synchronized
did not target the blocked node, an exception was raised. It seems that the two locking mechanisms are incompatible.
This resulted in a strange error message that didn't help me much.
source to share