Chapter 11 · Transactions, Isolation, Locking, Deadlocks, Retries, and Consistency
Bookmarks and Causal Consistency Across Sessions/Cluster Routing
Apply bookmarks only where AtlasMart workflows need causal read-after-write guarantees, and keep their scope separate from isolation and retry semantics.
Learning outcomes
In a single local Community server, a read after a committed write is straightforward. In a cluster, a later read can be routed differently. A bookmark is a causal dependency token: it tells subsequent work not to run until the database state represented by the bookmark has been established.
Explain bookmarks as causal dependencies rather than transaction IDs or global serialization tokens.
Use same-session causal chaining and explicit bookmark propagation across sessions.
Distinguish bolt:// direct-server URIs from neo4j:// routing intent at the application level.
Understand BookmarkManager convenience and its possible latency cost.
Provide a deterministic Community simulation while clearly separating true cluster routing behavior.
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.
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. A bookmark represents “at least this causal state”
After a committed transaction, the driver can receive a bookmark. Supplying that bookmark to later work means the later query waits until the represented state is available before executing. It does not mean every transaction in the system is globally serialized, and it does not replace transaction isolation.
| Claim | Correct? | Why |
|---|---|---|
| Bookmark guarantees a causally later read can observe the represented committed state | Yes | The later work waits for that state to be established |
| Bookmark makes every concurrent transaction globally serializable | No | It expresses causal dependency, not a universal serial order |
| Same session usually carries causal ordering automatically | Yes | Session bookmark handling chains its own work |
| BookmarkManager across all queries is free | No | Current driver docs warn that waiting for latest propagated state can add latency |
2. Same-session read-after-write
with driver.session(database='neo4j') as session: session.execute_write( lambda tx: tx.run( "MERGE (r:Reservation {reservationId:$id}) " "SET r.labTag='ch11',r.status='CONFIRMED'", id='CH11-R-CAUSAL' ).consume() ) row=session.execute_read( lambda tx: tx.run( "MATCH (r:Reservation {reservationId:$id}) RETURN r.status AS status", id='CH11-R-CAUSAL' ).single() ) print(row['status'])
3. Explicitly carry causality to a new session
with driver.session(database='neo4j') as writer: writer.execute_write(lambda tx: tx.run( "MATCH (s:Stock {stockId:'CH11-ST-001'}) SET s.version=s.version+1" ).consume()) bookmarks = writer.last_bookmarks()with driver.session(database='neo4j', bookmarks=bookmarks) as reader: row = reader.execute_read(lambda tx: tx.run( "MATCH (s:Stock {stockId:'CH11-ST-001'}) RETURN s.version AS version" ).single()) print(row['version'])
For auto-commit Session.run(), asking for
last_bookmarks() may consume the current result so
the transaction can complete and return its bookmark.
4. Cluster routing is optional in this chapter, not fabricated
A routed neo4j:// driver can direct reads and
writes according to cluster routing information; Community’s
mandatory lab cannot reproduce multi-server lag or routing.
Therefore the local exercise proves bookmark APIs and causal
chaining only. A licensed Enterprise/Aura cluster exercise may
additionally write through a routed driver, open a causally
chained read session and record the server/routing context plus
observed read-after-write behavior.
cluster_driver = GraphDatabase.driver( 'neo4j://cluster-entry:7687', auth=('neo4j','<secret>'))# Use the same bookmark patterns shown above; do not hard-code production secrets.
5. Deliberately wrong: use bookmarks everywhere “for consistency”
Unnecessary global bookmark chaining can make unrelated work wait for the latest causal state and increase latency. Define which API flows actually require read-your-writes or causal dependency, then propagate bookmarks only across those flows. A reporting query that has no causal dependency on an immediately preceding checkout write should not automatically inherit that dependency.
Check your understanding
- What does a bookmark represent?
- Is a bookmark a global serializability guarantee?
- What is the easiest way to keep closely related work causally chained?
- Can Community reproduce cluster routing/secondary lag?
- Why not use a global BookmarkManager indiscriminately?
Review the answers
1. A token for a committed database state that later causal work must wait to have established before execution.
2. No.
3. Keep it in the same driver session when practical.
4. No. The mandatory lab demonstrates bookmark API mechanics locally; true routed behavior needs a cluster/Aura environment.
5. It can make unrelated queries wait for propagated state and add latency.
Summary and next step
Bookmarks order dependent work; they do not repair contention or make retries safe. The final lesson puts all transaction mechanisms into one load test whose first success criterion is invariant preservation, not throughput.
Authoritative references
- Current Neo4j versions — Release/LTS snapshot used for this chapter.
- Database internals and transactional behavior — ACID behavior, read-committed isolation and transaction internals.
- Database transactions — Transaction lifecycle, memory and completion behavior.
- Concurrent data access — Locks, lost updates, contention, deadlocks and lock timeout semantics.
- Show and terminate transactions — SHOW TRANSACTIONS visibility and transaction diagnostics.
- Neo4j Python Driver 6.3 API — Current official Python driver, Bolt compatibility and transaction APIs.
- Python driver managed transactions — Managed transaction functions, retries and explicit transaction patterns.
- Python driver bookmarks — Bookmark propagation and causal chaining across sessions.