Skip to content

Commit daf33f6

Browse files
authored
Reword the content left on the Transaction mgmt page after moving parts of it to the operations manual (#132) (#134)
1 parent 63599e9 commit daf33f6

File tree

3 files changed

+37
-191
lines changed

3 files changed

+37
-191
lines changed

modules/ROOT/pages/index.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ It covers the following topics:
1010
* xref:extending-neo4j/index.adoc[] -- How to build unmanaged extensions and procedures.
1111
* xref:java-embedded/index.adoc[] -- Instructions on embedding Neo4j in .
1212
* xref:traversal-framework/index.adoc[] -- A walkthrough of the traversal framework.
13-
* xref:transaction-management.adoc[] -- Details on transaction semantics in Neo4j.
13+
* xref:transaction-management.adoc[] -- Examples on transaction management in Neo4j.
1414
* xref:jmx-metrics.adoc[] -- How to monitor Neo4j with JMX and a reference of available metrics.
1515
1616
[TIP]

modules/ROOT/pages/java-embedded/unique-nodes.adoc

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,27 @@
66

77
This describes how to ensure the uniqueness of a property when creating nodes.
88

9-
For an overview of unique nodes, see xref:transaction-management.adoc#transactions-unique-nodes[Transaction management -> Creating unique nodes].
9+
[[transactions-unique-nodes]]
10+
== Creating unique nodes
11+
12+
In many use cases, a certain level of uniqueness is desired among entities.
13+
For example, only one user with a certain email address may exist in a system.
14+
If multiple concurrent threads naively try to create the user, duplicates will be created.
15+
16+
The following are the main strategies for ensuring uniqueness, and they all work across cluster and single-instance deployments.
17+
18+
19+
[[transactions-unique-nodes-singlethread]]
20+
=== Single thread
21+
22+
By using a single thread, no two threads even try to create a particular entity simultaneously.
23+
In a cluster, an external single-threaded client can perform the operations.
24+
25+
26+
[[transactions-get-or-create]]
27+
=== Get or create
28+
29+
Defining a uniqueness constraint and using the Cypher `MERGE` clause is the most efficient way to _get or create_ a unique node.
1030

1131
[TIP]
1232
====
@@ -62,3 +82,5 @@ You might also be tempted to use Java synchronization for pessimistic locking, b
6282
By mixing locks in Neo4j and the Java runtime, it is possible to produce deadlocks that are not detectable by Neo4j.
6383
As long as all locking is done by Neo4j, all deadlocks will be detected and avoided.
6484

85+
For more information on locks and deadlocks, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-internals/locks-deadlocks.adoc#_locks[Operations Manual -> Locks and deadlocks^].
86+

modules/ROOT/pages/transaction-management.adoc

Lines changed: 13 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
:description: Neo4j transaction management, including interaction cycle, isolation levels, default locking behavior, deadlocks, delete semantics, creating unique nodes, and transaction events.
1+
:description: Neo4j transaction management, creating unique nodes, and transaction events.
22

33
:org-neo4j-graphdb-event-TransactionEventListener: {neo4j-javadocs-base-uri}/org/neo4j/graphdb/event/TransactionEventListener.html
44
:org-neo4j-graphdb-event-TransactionData: {neo4j-javadocs-base-uri}/org/neo4j/graphdb/event/TransactionData.html
@@ -8,31 +8,17 @@
88
[[transaction-management]]
99
= Transaction management
1010

11-
This topic describes transactional management and behavior.
12-
13-
To fully maintain data integrity and ensure good transactional behavior, Neo4j DBMS supports the **ACID** properties:
14-
15-
* **A**tomicity -- If a part of a transaction fails, the database state is left unchanged.
16-
* **C**onsistency -- Every transaction leaves the database in a consistent state.
17-
* **I**solation -- During a transaction, modified data cannot be accessed by other operations.
18-
* **D**urability -- The DBMS can always recover the results of a committed transaction.
19-
20-
Specifically:
21-
22-
* All database operations that access the graph, indexes, or schema must be performed in a transaction.
23-
* The default isolation level is _read-committed isolation level_.
24-
* Data retrieved by traversals is not protected from modification by other transactions.
25-
* Non-repeatable reads may occur (i.e., only write locks are acquired and held until the end of the transaction).
26-
* One can manually acquire write locks on nodes and relationships to achieve a higher level of isolation -- _serialization isolation level_.
27-
* Locks are acquired at the Node and Relationship levels.
28-
* Deadlock detection is built into the core transaction management.
11+
[IMPORTANT]
12+
====
13+
This page describes only some specific aspects of transaction management when used with the Neo4j Java API and provides some examples of how to avoid deadlocks, and how to register a transaction event listener for a specific database and perform basic operations on top of the transaction change set.
2914
15+
Therefore, it is highly recommended that you read link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-internals/[Operations Manual -> Database internals and transactional behavior] before you continue reading this page.
16+
====
3017

31-
[[transactions-interaction]]
32-
== Interaction cycle
18+
[[transactions-overview]]
19+
== Overview
3320

34-
There are database operations that must be performed in a transaction to ensure the ACID properties.
35-
Specifically, operations that access the graph, indexes, or schema are such operations.
21+
Database operations that access the graph, indexes, or schema are performed in a transaction to ensure the ACID properties.
3622
Transactions are single-threaded, confined, and independent.
3723
Multiple transactions can be started in a single thread and they are independent of each other.
3824

@@ -44,8 +30,8 @@ The interaction cycle of working with transactions follows the steps:
4430

4531
[NOTE]
4632
====
47-
It is crucial to finish each transaction.
48-
The locks or memory acquired by a transaction are only released upon completion.
33+
It is crucial to finish each transaction because the link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-internals/locks-deadlocks.adoc#_locks[locks^] or memory acquired by a transaction are only released upon completion.
34+
For more information on locks and deadlocks, see link:{neo4j-docs-base-uri}/operations-manual/{page-version}/database-internals/locks-deadlocks.adoc#_locks[Operations Manual -> Locks and deadlocks^].
4935
====
5036

5137
The idiomatic use of transactions in Neo4j is to use a `try-with-resources` statement and declare `transaction` as one of the resources.
@@ -61,125 +47,8 @@ All modifications performed in a transaction are kept in memory.
6147
This means that very large updates must be split into several transactions to avoid running out of memory.
6248
====
6349

64-
65-
[[transactions-isolation]]
66-
== Isolation levels
67-
68-
Transactions in Neo4j use a _read-committed isolation level_, which means they see data as soon as it has been committed but cannot see data in other transactions that have not yet been committed.
69-
This type of isolation is weaker than serialization but offers significant performance advantages while being sufficient for the overwhelming majority of cases.
70-
71-
In addition, the Neo4j Java API enables explicit locking of nodes and relationships.
72-
Using locks allows simulating the effects of higher levels of isolation by obtaining and releasing locks explicitly.
73-
For example, if a write lock is taken on a common node or relationship, then all transactions are serialized on that lock -- giving the effect of a _serialization isolation level_.
74-
75-
76-
[[transactions-isolation-lostupdates]]
77-
=== Lost updates in Cypher
78-
79-
In Cypher it is possible to acquire write locks to simulate improved isolation in some cases.
80-
Consider the case where multiple concurrent Cypher queries increment the value of a property.
81-
Due to the limitations of the _read-committed isolation level_, the increments might not result in a deterministic final value.
82-
If there is a direct dependency, Cypher automatically acquires a write lock before reading.
83-
A direct dependency is when the right-hand side of `SET` has a dependent property read in the expression or the value of a key-value pair in a literal map.
84-
85-
For example, if you run one of the following queries by one hundred concurrent clients, it will increment the property `n.prop` to 100 because Cypher will automatically acquire a write lock.
86-
87-
.Cypher can acquire a write lock
88-
====
89-
The following example requires a write lock, and Cypher automatically acquires one:
90-
91-
[source, cypher, role="noheader"]
92-
----
93-
MATCH (n:Example {id: 42})
94-
SET n.prop = n.prop + 1
95-
----
96-
====
97-
98-
.Cypher can acquire a write lock
99-
====
100-
This example also requires a write lock, and Cypher automatically acquires one:
101-
102-
[source, cypher, role="noheader"]
103-
----
104-
MATCH (n)
105-
SET n += {prop: n.prop + 1}
106-
----
107-
====
108-
109-
Due to the complexity of determining such a dependency in the general case, Cypher does not cover all cases.
110-
If you run one of the following queries concurrently 100 times, the final value of `n.prop` will most probably be less than 100.
111-
112-
.Complex Cypher
113-
====
114-
Variable depending on results from reading the property in an earlier statement:
115-
116-
[source, cypher, role="noheader"]
117-
----
118-
MATCH (n)
119-
WITH n.prop AS p
120-
// ... operations depending on p, producing k
121-
SET n.prop = k + 1
122-
----
123-
====
124-
125-
.Complex Cypher
126-
====
127-
Circular dependency between properties read and written in the same query:
128-
129-
[source, cypher, role="noheader"]
130-
----
131-
MATCH (n)
132-
SET n += {propA: n.propB + 1, propB: n.propA + 1}
133-
----
134-
====
135-
136-
To ensure deterministic behavior also in the more complex cases, it is necessary to explicitly acquire a write lock on the node in question.
137-
In Cypher there is no explicit support for this, but it is possible to work around this limitation by writing to a temporary property.
138-
139-
.Explicitly acquire a write lock
140-
====
141-
This example acquires a write lock for the node by writing to a dummy property before reading the requested value:
142-
143-
[source, cypher, role="noheader"]
144-
----
145-
MATCH (n:Example {id: 42})
146-
SET n._LOCK_ = true
147-
WITH n.prop AS p
148-
// ... operations depending on p, producing k
149-
SET n.prop = k + 1
150-
REMOVE n._LOCK_
151-
----
152-
====
153-
154-
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 are lost due to enforced serialization of all concurrent queries on that specific node.
155-
156-
157-
[[transactions-locking]]
158-
== Default locking behavior
159-
160-
* When adding, changing, or removing a property on a node or relationship, a write lock is taken on the specific node or relationship.
161-
* When creating or deleting a node a write lock is taken for the specific node.
162-
* When creating or deleting a relationship a write lock is taken on the specific relationship and both its nodes.
163-
164-
The locks are added to the transaction and released when the transaction finishes.
165-
166-
167-
[[transactions-deadlocks]]
168-
== Deadlocks
169-
170-
Since locks are used, deadlocks can happen.
171-
Neo4j, however, detects any deadlock (caused by acquiring a lock) before they happen and throws an exception.
172-
The transaction is marked for rollback before the exception is thrown.
173-
All locks acquired by the transaction are still held but will be released when the transaction finishes.
174-
Once the locks are released, other transactions that were waiting for locks held by the transaction causing the deadlock can proceed.
175-
You can then retry the work performed by the transaction causing the deadlock if needed.
176-
177-
Experiencing frequent deadlocks is an indication of concurrent write requests happening in such a way that it is not possible to execute them while at the same time living up to the intended isolation and consistency.
178-
The solution is to make sure concurrent updates happen reasonably.
179-
For example, given two specific nodes (A and B), adding or deleting relationships to both these nodes in random order for each transaction results in deadlocks when two or more transactions do that concurrently.
180-
One option is to make sure that updates always happen in the same order (first A then B).
181-
Another option is to make sure that each thread/transaction does not have any conflicting writes to a node or relationship as some other concurrent transaction.
182-
This can, for example, be achieved by letting a single thread do all updates of a specific type.
50+
[[transactions-deadlocks-code]]
51+
== Deadlock handling an example
18352

18453
[IMPORTANT]
18554
====
@@ -188,10 +57,6 @@ Since all operations in the Neo4j API are thread-safe unless specified otherwise
18857
Other code that requires synchronization should be synchronized in such a way that it never performs any Neo4j operation in the synchronized block.
18958
====
19059

191-
192-
[[transactions-deadlocks-code]]
193-
=== Deadlock handling an example
194-
19560
The following is an example of how deadlocks can be handled in procedures, server extensions, or when using Neo4j embedded.
19661

19762
[TIP]
@@ -270,47 +135,6 @@ else
270135
----
271136
====
272137

273-
[[transactions-delete]]
274-
== Delete semantics
275-
276-
When deleting a node or a relationship all properties for that entity will be automatically removed but the relationships of a node will not be removed.
277-
Neo4j enforces a constraint (upon commit) that all relationships must have a valid start node and end node.
278-
In effect, this means that trying to delete a node that still has relationships attached to it will throw an exception upon commit.
279-
It is, however, possible to choose in which order to delete the node and the attached relationships as long as no relationships exist when the transaction is committed.
280-
281-
The delete semantics can be summarized as follows:
282-
283-
* All properties of a node or relationship will be removed when it is deleted.
284-
* A deleted node cannot have any attached relationships when the transaction commits.
285-
* It is possible to acquire a reference to a deleted relationship or node that has not yet been committed.
286-
* Any write operation on a node or relationship after it has been deleted (but not yet committed) will throw an exception.
287-
* Trying to acquire a new or work with an old reference to a deleted node or relationship after commit, will throw an exception.
288-
289-
290-
[[transactions-unique-nodes]]
291-
== Creating unique nodes
292-
293-
In many use cases, a certain level of uniqueness is desired among entities.
294-
For example, only one user with a certain email address may exist in a system.
295-
If multiple concurrent threads naively try to create the user, duplicates will be created.
296-
297-
The following are the main strategies for ensuring uniqueness, and they all work across cluster and single-instance deployments.
298-
299-
300-
[[transactions-unique-nodes-singlethread]]
301-
=== Single thread
302-
303-
By using a single thread, no two threads even try to create a particular entity simultaneously.
304-
In a cluster, an external single-threaded client can perform the operations.
305-
306-
307-
[[transactions-get-or-create]]
308-
=== Get or create
309-
310-
Defining a uniqueness constraint and using the Cypher `MERGE` clause is the most efficient way to _get or create_ a unique node.
311-
See xref:java-embedded/unique-nodes.adoc[] for more information.
312-
313-
314138
[[transactions-events]]
315139
== Transaction events
316140

0 commit comments

Comments
 (0)