Sebastian Schlicht
Sebastian Schlicht

Reputation: 357

Lock released causes dead lock in Neo4j

Consider a social network graph with users and news items. There are multiple methods that modify the graph and they can be called concurrently.
I implemented a locking manager to avoid any dead locks. It locks users ordered from lowest to highest user id, that is stored as a property in user nodes. Locks are released in reverse order.

Nevertheless I get the following DeadLockDetectedException when executing a specific method concurrently:

org.neo4j.kernel.TopLevelTransaction@41aaf8e9:
  locking manager locked [NODE(0) [#1], NODE(5) [#3]]
org.neo4j.kernel.TopLevelTransaction@41aaf8e9:
  locking manager released [NODE(5) [#3], NODE(0) [#1]]
org.neo4j.kernel.TopLevelTransaction@41aaf8e9:
  locking manager locked [NODE(1) [#2]
Exception in thread "Thread-3" org.neo4j.kernel.TopLevelTransaction@6242de17: 
  org...DeadlockDetectedException:
  LockClient[18] can't wait on resource RWLock[NODE(1)] since => 
  LockClient[18] <-[:HELD_BY]- RWLock[NODE(5)] <-[:WAITING_FOR]- LockClient[20] <-[:HELD_BY]- RWLock[NODE(1)]
org.neo4j.kernel.TopLevelTransaction@7007944f:
  locking manager locked [NODE(1) [#2], NODE(5) [#3]]

As you can see, a transaction locks two user nodes (id 1 and 3). A bit later it tries to lock another user node (id 2), but this lock can't be acquired: There is a second transaction that has locked the user with id 2 and is waiting to lock the user with id 3.
Plausible, this would be a dead lock.

However, the code and the previous log message tells another story: Before the transaction tries to lock the user with id 2, it released the two locks it has acquired previously. Here is the corresponding code:

Transaction tx = graph.beginTransaction();
  // locks NODE(0) and NODE(5)
Lock[] locks = LockManager.lock(tx, following, followed);
boolean result = removeFollowship(following, followed);
  // releases NODE(5) and NODE(0)
LockManager.releaseLocks(locks);
...
  // tries to lock NODE(1)
List<Lock> locks = LockManager.lock(tx, replicaLayer);
try {
    return addStatusUpdate(author, statusUpdate);
} finally {
    LockManager.releaseLocks(locks);
}
...
tx.success();

The lock manager locks nodes via tx.acquireWriteLock(node) and releases locks via lock.release().
Is there anything I'm not aware of? For example, is there a delay between the call of lock.release and new locking of the corresponding node?

Upvotes: 0

Views: 426

Answers (1)

Michael Hunger
Michael Hunger

Reputation: 41676

Locks are really just released when the transaction finishes.

That's why until your tx.close() is called it still holds the locks.

Upvotes: 1

Related Questions