18.2. Isolation levels

Transactions in Neo4j use a read-committed isolation level, which means they will see data as soon as it has been committed and will not see data in other transactions that have not yet been committed. This type of isolation is weaker than serialization but offers significant performance advantages whilst being sufficient for the overwhelming majority of cases.

In addition, the Neo4j Java API (see Advanced Usage) enables explicit locking of nodes and relationships. Using locks gives the opportunity to simulate the effects of higher levels of isolation by obtaining and releasing locks explicitly. For example, if a write lock is taken on a common node or relationship, then all transactions will serialize on that lock — giving the effect of a serialization isolation level.

Lost Updates in Cypher

In Cypher it is possible to acquire write locks to simulate improved isolation in some cases. Consider the case where multiple concurrent Cypher queries increment the value of a property. Due to the limitations of the read-committed isolation level, the increments will not result in a deterministic final value.

For example, the following query, if run by one hundred concurrent clients, will very likely not increment the property n.prop to 100, but some value lower than 100.

MATCH (n:X {id: 42})
SET n.prop = n.prop + 1

This is because all queries will read the value of n.prop within their own transaction. They will not see the incremented value from any other transaction that has not yet committed. In the worst case scenario the final value could be as low as 1, if all threads perform the read before any has committed their transaction.

To ensure deterministic behavior, it is necessary to grab a write lock on the node in question. In Cypher there is no explicit support for this, but we can work around this limitation by writing to a temporary property.

MATCH (n:X {id: 42})
SET n._LOCK_ = true
SET n.prop = n.prop + 1

The existence of the SET n._LOCK_ statement before the read of the n.prop read ensures the lock is acquired before the read action, and no updates will be lost due to enforced serialization of all concurrent queries on that specific node.