Skip to content

Commit

Permalink
Merge pull request #6451 from jjaderberg/3.0-procedure-docs
Browse files Browse the repository at this point in the history
Document procedures
  • Loading branch information
pontusmelke committed Feb 18, 2016
2 parents c7f8b27 + 074f3e3 commit 3b29456
Show file tree
Hide file tree
Showing 11 changed files with 697 additions and 168 deletions.
91 changes: 61 additions & 30 deletions community/kernel/src/docs/dev/transactions.asciidoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[[transactions]]
Transaction Management
======================
= Transaction Management

In order to fully maintain data integrity and ensure good transactional behavior, Neo4j supports the ACID properties:

Expand All @@ -12,15 +11,16 @@ In order to fully maintain data integrity and ensure good transactional behavior
Specifically:

* All database operations that access the graph, indexes, or the schema must be performed in a transaction.
* The default isolation level is +READ_COMMITTED+.
* The default isolation level is `READ_COMMITTED`.
* Data retrieved by traversals is not protected from modification by other transactions.
* Non-repeatable reads may occur (i.e., only write locks are acquired and held until the end of the transaction).
* One can manually acquire write locks on nodes and relationships to achieve higher level of isolation (+SERIALIZABLE+).
* One can manually acquire write locks on nodes and relationships to achieve higher level of isolation (`SERIALIZABLE`).
* Locks are acquired at the Node and Relationship level.
* Deadlock detection is built into the core transaction management.


[[transactions-interaction]]
== Interaction cycle ==
== Interaction cycle

All database operations that access the graph, indexes, or the schema must be performed in a transaction.
Transactions are thread confined and can be nested as “flat nested transactions”.
Expand Down Expand Up @@ -51,8 +51,9 @@ It will be tied to a thread and when that thread gets scheduled to perform work
If the leaked transaction state is “marked for rollback” (which will happen if a deadlock was detected) no more work can be performed on that transaction.
Trying to do so will result in error on each call to a write operation.


[[transactions-isolation]]
== Isolation levels ==
== 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.
Expand All @@ -61,7 +62,8 @@ In addition, the Neo4j Java API (see <<advanced-usage>>) enables explicit lockin
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 ===

=== 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.
Expand Down Expand Up @@ -121,19 +123,22 @@ REMOVE n._LOCK_

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.


[[transactions-locking]]
== Default locking behavior ==
== Default locking behavior

* When adding, changing or removing a property on a node or relationship a write lock will be taken on the specific node or relationship.
* When creating or deleting a node a write lock will be taken for the specific node.
* When creating or deleting a relationship a write lock will be taken on the specific relationship and both its nodes.

The locks will be added to the transaction and released when the transaction finishes.


[[transactions-deadlocks]]
== Deadlocks ==
== Deadlocks


=== Understanding deadlocks ===
=== Understanding deadlocks

Since locks are used it is possible for deadlocks to happen.
Neo4j will however detect any deadlock (caused by acquiring a lock) before they happen and throw an exception.
Expand All @@ -150,12 +155,15 @@ Another solution is to make sure that each thread/transaction does not have any
This can for example be achieved by letting a single thread do all updates of a specific type.

[IMPORTANT]
--
Deadlocks caused by the use of other synchronization than the locks managed by Neo4j can still happen.
Since all operations in the Neo4j API are thread safe unless specified otherwise, there is no need for external synchronization.
Other code that requires synchronization should be synchronized in such a way that it never performs any Neo4j operation in the synchronized block.
--


[[transactions-deadlocks-code]]
=== Deadlock handling example code ===
=== Deadlock handling example code

Below you'll find examples of how deadlocks can be handled in server extensions/plugins or when using Neo4j embedded.

Expand All @@ -169,8 +177,9 @@ When dealing with deadlocks in code, there are several issues you may want to ad

In the following sections you'll find example code in Java which shows how this can be implemented.


[[transactions-deadlocks-template]]
==== Handling deadlocks using TransactionTemplate ====
==== Handling deadlocks using TransactionTemplate

If you don't want to write all the code yourself, there is a class called +link:javadocs/org/neo4j/helpers/TransactionTemplate.html[TransactionTemplate]+ that will help you achieve what's needed.
Below is an example of how to create, customize, and use this template for retries in transactions.
Expand Down Expand Up @@ -198,8 +207,9 @@ The operations that could lead to a deadlock should go into the `apply` method.
The `TransactionTemplate` uses a fluent API for configuration, and you can choose whether to set everything at once, or (as in the example) provide some details just before using it.
The template allows setting a predicate for what exceptions to retry on, and also allows for easy monitoring of events that take place.


[[transactions-deadlocks-loop]]
==== Handling deadlocks using a retry loop ====
==== Handling deadlocks using a retry loop

If you want to roll your own retry-loop code, see below for inspiration.
Here's an example of what a retry block might look like:
Expand All @@ -213,8 +223,9 @@ tag=retry

The above is the gist of what such a retry block would look like, and which you can customize to fit your needs.


[[transactions-delete]]
== Delete semantics ==
== Delete semantics

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.

Expand All @@ -231,19 +242,23 @@ The delete semantics can be summarized in the following bullets:
* Any write operation on a node or relationship after it has been deleted (but not yet committed) will throw an exception
* After commit trying to acquire a new or work with an old reference to a deleted node or relationship will throw an exception.


[[transactions-unique-nodes]]
== Creating unique nodes ==
== Creating unique nodes
In many use cases, a certain level of uniqueness is desired among entities.
You could for instance imagine that only one user with a certain e-mail address may exist in a system.
If multiple concurrent threads naively try to create the user, duplicates will be created.
There are three main strategies for ensuring uniqueness, and they all work across High Availability and single-instance deployments.

=== Single thread ===

=== Single thread

By using a single thread, no two threads will even try to create a particular entity simultaneously.
On High Availability, an external single-threaded client can perform the operations on the cluster.


[[transactions-get-or-create]]
=== Get or create ===
=== Get or create

The preferred way to get or create a unique node is to use unique constraints and Cypher.
See <<tutorials-java-embedded-unique-get-or-create>> for more information.
Expand All @@ -254,24 +269,40 @@ entity uniqueness can be guaranteed using a legacy index. Here the legacy index
See <<tutorials-java-embedded-unique-get-or-create-with-factory>> for how to do this using the core Java API.
When using the REST API, see <<rest-api-unique-indexes>>.

=== Pessimistic locking ===

=== Pessimistic locking

[IMPORTANT]
While this is a working solution, please consider using the preferred <<transactions-get-or-create>> instead.
--
While this is a working solution, please consider using the preferred method method outlined above instead.
--

By using explicit, pessimistic locking, unique creation of entities can be achieved in a multi-threaded environment.
It is most commonly done by locking on a single or a set of common nodes.

See <<tutorials-java-embedded-unique-pessimistic>> for how to do this using the core Java API.


[[transactions-events]]
== Transaction events ==

Transaction event handlers can be registered to receive Neo4j Transaction events.
Once it has been registered at a +GraphDatabaseService+ instance it will receive events about what has happened in each transaction which is about to be committed.
Handlers won't get notified about transactions which haven't performed any write operation or won't be committed (either if +Transaction#success()+ hasn't been called or the transaction has been marked as failed +Transaction#failure()+.
Right before a transaction is about to be committed the +beforeCommit+ method is called with the entire diff of modifications made in the transaction.
At this point the transaction is still running so changes can still be made. However there's no guarantee that other handlers will see such changes since the order in which handlers are executed is undefined.
This method can also throw an exception and will, in such a case, prevent the transaction from being committed (where a call to +afterRollback+ will follow).
If +beforeCommit+ is successfully executed in all registered handlers the transaction will be committed and the +afterCommit+ method will be called with the same transaction data as well as the object returned from +beforeCommit+.
In +afterCommit+ the transaction has been closed and so accessing data outside of what +TransactionData+ covers requires a new transaction to be opened.
+TransactionEventHandler+ gets notified about transactions that has any change accessible via +TransactionData+ so some indexing and schema changes will not be triggering these events.
== Transaction events

A transaction event handler can be registered to receive Neo4j transaction events.
Once it has been registered at a `GraphDatabaseService` instance it receives events for transactions before they are committed.
Handlers get notified about transactions that have performed any write operation, and that will be committed.
If `Transaction#success()` has not been called or the transaction has been marked as failed `Transaction#failure()` it will be rolled back, and no events are sent to the Handler.

Before a transaction is committed the Handler's `beforeCommit` method is called with the entire diff of modifications made in the transaction.
At this point the transaction is still running, so changes can still be made.
The method may also throw an exception, which will prevent the transaction from being committed.
If the transaction is rolled back, a call to the handler's `afterRollback` method will follow.

[CAUTION]
--
The order in which handlers are executed is undefined -- there is no guarantee that changes made by one handler will be seen by other handlers.
--

If `beforeCommit` is successfully executed in all registered handlers the transaction is committed and the `afterCommit` method is called with the same transaction data.
This call also includes the object returned from `beforeCommit`.

In `afterCommit` the transaction has been closed and access to anything outside `TransactionData` requires a new transaction to be opened.
A `TransactionEventHandler` gets notified about transactions that have any changes accessible via `TransactionData` so some indexing and schema changes will not be triggering these events.

This file was deleted.

2 changes: 1 addition & 1 deletion manual/cypher/cypher-docs/src/docs/dev/general.asciidoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[[query-general]]
= General Clauses =
= General Clauses

:leveloffset: 2

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
= Call

[abstract]
--
The `CALL` clause is used to call a procedure deployed in the database.
--

The examples showing how to use arguments when invoking procedures all use the following procedure:

Expand Down Expand Up @@ -31,4 +33,4 @@ include::call-a-procedure-with-parameter-arguments.asciidoc[]

:leveloffset: 2

include::call-a-procedure-with-mixed-literal-and-parameter-arguments.asciidoc[]
include::call-a-procedure-with-mixed-literal-and-parameter-arguments.asciidoc[]

0 comments on commit 3b29456

Please sign in to comment.