Chapter 11 · Transactions, Isolation, Locking, Deadlocks, Retries, and Consistency

Locks on Nodes/Relationships/Schema, Contention Patterns, Dense Graph Hotspots, and Deadlock Detection

Diagnose AtlasMart contention and deadlocks from resource overlap, lock lifetime and transaction order rather than from generic “database slowness.”

Advanced155–190 minutesLock contention + deadlock labNeo4j 2026.07.1 Community · Cypher 25Python driver 6.3.x optionalLast reviewed: September 2026

Learning outcomes

Two reservations can be individually correct yet contend on the same stock entity. Neo4j locks graph entities and internal structures to preserve consistency; those locks can serialize hot writes or form a deadlock cycle when transactions acquire overlapping resources in incompatible order.

01

Explain default node/relationship locking at the level applications can safely rely on.

02

Distinguish lock contention from deadlock and identify the business shapes that create hotspots.

03

Understand automatic dependent-property locking and the lost-update boundary.

04

Reproduce a reversible two-resource deadlock without APOC or production data.

05

Use Community-safe transaction evidence and label Enterprise-only active-lock introspection correctly.

Chapter 11 baseline · reviewed 9 September 2026

The mandatory lab continues Neo4j Community 2026.07.1, database neo4j, explicit CYPHER 25 for version-sensitive examples, container atlasmart-neo4j, authentication enabled, loopback Bolt/HTTP endpoints, no mandatory APOC/GDS plugin, and AtlasMart domain identifiers established in Chapters 01–10. Neo4j 5.26.30 remains the LTS comparison line. Optional application examples use the official Python driver 6.3.x.

Concurrency evidence note

This generation environment does not run Docker/Neo4j, so no deadlock frequency, wait duration, retry count, latency, lock list or cluster routing output is invented. The labs define deterministic invariants and controlled concurrency procedures that you execute on the disposable local instance. Community can use SHOW TRANSACTIONS for its own work; dbms.listActiveLocks() is currently Enterprise-only and is therefore optional evidence, not a mandatory lab dependency.

1. Locks exist to serialize conflicting modifications

Updating a node property takes a node write lock; updating a relationship property takes a relationship lock. Relationship creation/deletion also protects its endpoints according to the current storage representation. Additional internal locks may be taken to maintain indexes and graph structures; application code should not depend on undocumented internal lock ordering.

Workload shape Likely symptom Design question
Many writers update one Stock node Wait time / throughput plateau Can inventory be partitioned by store/SKU/bucket while preserving invariants?
Transactions touch A then B and others B then A Deadlock risk Can every workflow acquire resources in a deterministic order?
Long transaction holds changed entities Long waits and memory retention Can work be shortened or external waits moved outside the transaction?
Dense relationship churn High concurrent structural work Are relationship semantics/ownership appropriately partitioned?

2. Read committed still needs lost-update reasoning

Cypher automatically acquires a write lock before reading a property when the right side of a SET directly depends on that property, such as SET s.reserved = s.reserved + $qty. More complex workflows can read a value into an earlier variable and later write a derived value without such a direct dependency. Current Neo4j documentation shows a dummy-property write/remove as a workaround for explicitly taking a node write lock before the dependent read.

Cypher · explicit lock workaround for a complex reservation calculation
CYPHER 25MATCH (s:Stock {stockId:'CH11-ST-001'})SET s._ch11_lock = trueREMOVE s._ch11_lockWITH s, s.onHand - s.reserved AS availableWITH s, available, CASE WHEN available >= $qty THEN $qty ELSE 0 END AS acceptedSET s.reserved = s.reserved + accepted,    s.version = s.version + CASE WHEN accepted > 0 THEN 1 ELSE 0 ENDRETURN available,accepted,s.reserved,s.version;

3. Contention waits; deadlock cycles

Contention means one transaction waits for a lock and can proceed when the holder finishes. A deadlock means transactions form a wait cycle and none can progress. Neo4j detects the cycle and aborts one transaction with Neo.TransientError.Transaction.DeadlockDetected; current releases also report GQLSTATUS 50N05.

Python · deterministic two-resource deadlock harness
import threading, timefrom neo4j import GraphDatabaseURI='bolt://127.0.0.1:7687'AUTH=('neo4j','atlasmart-course-2026')barrier = threading.Barrier(2)def worker(first, second):    with driver.session(database='neo4j') as session:        tx = session.begin_transaction(timeout=10)        try:            tx.run("MATCH (n:Counter {counterId:$id}) SET n.value=n.value+1", id=first).consume()            barrier.wait()            time.sleep(0.2)            tx.run("MATCH (n:Counter {counterId:$id}) SET n.value=n.value+1", id=second).consume()            tx.commit()            print(first, 'committed')        except Exception as exc:            print(first, type(exc).__name__, exc)            tx.rollback()with GraphDatabase.driver(URI, auth=AUTH) as driver:    t1=threading.Thread(target=worker,args=('CH11-A','CH11-B'))    t2=threading.Thread(target=worker,args=('CH11-B','CH11-A'))    t1.start(); t2.start(); t1.join(); t2.join()

Run only against the disposable Chapter 11 counters. Exact timing can vary; the invariant is the wait-cycle shape, not that every run must deadlock at precisely the same millisecond.

4. Observe blocked work without requiring Enterprise

Cypher · Community-safe transaction view
CYPHER 25SHOW TRANSACTIONSYIELD transactionId,currentQueryId,currentQuery,status,elapsedTime,waitTimeWHERE currentQuery CONTAINS 'CH11-'RETURN transactionId,currentQueryId,status,elapsedTime,waitTime,currentQuery;

On Enterprise, an administrator can additionally call dbms.listActiveLocks(currentQueryId). That procedure is Enterprise-only in the current Operations Manual, so the mandatory Community lab uses transaction state, controlled timing, driver exceptions and invariant checks instead.

5. Repair: deterministic acquisition order and shorter transactions

A common deadlock reduction strategy is to sort business resource identifiers and modify them in the same order in every transaction. This reduces lock-order cycles without changing isolation. It does not eliminate contention when all requests still target the same hot entity.

Check your understanding

  1. What is the difference between contention and deadlock?
  2. Which error identifies a detected Neo4j deadlock?
  3. Does every property read automatically lock the entity for the rest of the transaction?
  4. Is dbms.listActiveLocks a mandatory Community diagnostic?
  5. Why sort resources before updating several of them?
Review the answers

1. Contention is waiting that can resolve when a lock is released; deadlock is a cycle of waits that cannot resolve without aborting a participant.

2. Neo.TransientError.Transaction.DeadlockDetected; current releases also include GQLSTATUS 50N05.

3. No. Read committed does not make ordinary reads repeatable; automatic write-lock acquisition depends on the write dependency pattern.

4. No. It is currently Enterprise-only.

5. A consistent acquisition order reduces wait cycles and therefore deadlock risk.

Summary and next step

Neo4j detects deadlocks, but the application still needs a retry policy. Next we design managed-transaction retries so transient failure handling does not duplicate business or external effects.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.