Sessions & Transactions¶
All database activity is co-ordinated through two mechanisms: the Session
and the Transaction
.
A Transaction
is a unit of work that is either committed in its entirety or is rolled back on failure.
A Session
is a logical container for any number of causally-related transactional units of work.
Sessions automatically provide guarantees of causal consistency within a clustered environment but multiple sessions can also be causally chained if required.
Sessions¶
Sessions provide the top-level of containment for database activity. Session creation is a lightweight operation and sessions are not thread safe.
Connections are drawn from the Driver
connection pool as required; an idle session will not hold onto a connection.
Sessions will often be created and destroyed using a with block context. For example:
with driver.session() as session:
result = session.run("MATCH (a:Person) RETURN a.name")
# do something with the result...
To construct a Session
use the Driver.session()
method.
-
class
neo4j.
Session
¶ -
Session.
close
()¶ Close the session. This will release any borrowed resources, such as connections, and will roll back any outstanding transactions.
-
Session.
closed
()¶ Indicator for whether or not this session has been closed.
- Returns
True
if closed,False
otherwise.
-
Session.
run
(statement, parameters=None, **kwparameters)¶ Run a Cypher statement within an auto-commit transaction.
The statement is sent and the result header received immediately but the
StatementResult
content is fetched lazily as consumed by the client application.If a statement is executed before a previous
StatementResult
in the sameSession
has been fully consumed, the first result will be fully fetched and buffered. Note therefore that the generally recommended pattern of usage is to fully consume one result before executing a subsequent statement. If two results need to be consumed in parallel, multipleSession
objects can be used as an alternative to result buffering.For more usage details, see
Transaction.run()
.- Parameters
statement – template Cypher statement
parameters – dictionary of parameters
kwparameters – additional keyword parameters
- Returns
StatementResult
object
-
Session.
sync
()¶ Carry out a full send and receive.
- Returns
number of records fetched
-
Session.
detach
(result, sync=True)¶ Detach a result from this session by fetching and buffering any remaining records.
- Parameters
result –
sync –
- Returns
number of records fetched
-
Session.
next_bookmarks
()¶ The set of bookmarks to be passed into the next
Transaction
.
-
Session.
last_bookmark
()¶ The bookmark returned by the last
Transaction
.
-
Session.
has_transaction
()¶
-
Session.
begin_transaction
(bookmark=None, metadata=None, timeout=None)¶ Create a new
Transaction
within this session. Calling this method with a bookmark is equivalent to- Parameters
bookmark – a bookmark to which the server should synchronise before beginning the transaction
metadata –
timeout –
- Returns
new
Transaction
instance.- Raise
TransactionError
if a transaction is already open
-
Session.
read_transaction
(unit_of_work, *args, **kwargs)¶
-
Session.
write_transaction
(unit_of_work, *args, **kwargs)¶
-
Transactions¶
Neo4j supports three kinds of transaction: auto-commit transactions, explicit transactions and transaction functions. Each has pros and cons but if in doubt, use a transaction function.
Auto-commit Transactions¶
Auto-commit transactions are the simplest form of transaction, available via Session.run()
.
These are easy to use but support only one statement per transaction and are not automatically retried on failure.
Auto-commit transactions are also the only way to run PERIODIC COMMIT
statements, since this Cypher clause manages its own transactions internally.
def create_person(driver, name):
with driver.session() as session:
return session.run("CREATE (a:Person {name:$name}) "
"RETURN id(a)", name=name).single().value()
Explicit Transactions¶
Explicit transactions support multiple statements and must be created with an explicit Session.begin_transaction()
call.
This creates a new Transaction
object that can be used to run Cypher.
It also gives applications the ability to directly control commit and rollback activity.
-
class
neo4j.
Transaction
¶ -
Transaction.
run
(statement, parameters=None, **kwparameters)¶ Run a Cypher statement within the context of this transaction.
The statement is sent to the server lazily, when its result is consumed. To force the statement to be sent to the server, use the
Transaction.sync()
method.Cypher is typically expressed as a statement template plus a set of named parameters. In Python, parameters may be expressed through a dictionary of parameters, through individual parameter arguments, or as a mixture of both. For example, the run statements below are all equivalent:
>>> statement = "CREATE (a:Person {name:{name}, age:{age}})" >>> tx.run(statement, {"name": "Alice", "age": 33}) >>> tx.run(statement, {"name": "Alice"}, age=33) >>> tx.run(statement, name="Alice", age=33)
Parameter values can be of any type supported by the Neo4j type system. In Python, this includes
bool
,int
,str
,list
anddict
. Note however thatlist
properties must be homogenous.- Parameters
statement – template Cypher statement
parameters – dictionary of parameters
kwparameters – additional keyword parameters
- Returns
StatementResult
object- Raises
TransactionError – if the transaction is closed
-
Transaction.
sync
()¶ Force any queued statements to be sent to the server and all related results to be fetched and buffered.
- Raises
TransactionError – if the transaction is closed
-
success
¶ This attribute can be used to determine the outcome of a transaction on closure. Specifically, this will be either a COMMIT or a ROLLBACK. A value can be set for this attribute multiple times in user code before a transaction completes, with only the final value taking effect.
On closure, the outcome is evaluated according to the following rules:
__exit__
cleanly__exit__
with exceptiontx.close()
tx.commit()
tx.rollback()
None
COMMIT
ROLLBACK
ROLLBACK
COMMIT
ROLLBACK
True
COMMIT
COMMIT 1
COMMIT
COMMIT
ROLLBACK
False
ROLLBACK
ROLLBACK
ROLLBACK
COMMIT
ROLLBACK
- 1
While a COMMIT will be attempted in this scenario, it will likely fail if the exception originated from Cypher execution within that transaction.
-
Transaction.
close
()¶ Close this transaction, triggering either a COMMIT or a ROLLBACK, depending on the value of
success
.- Raises
TransactionError – if already closed
-
Transaction.
closed
()¶ Indicator to show whether the transaction has been closed. :returns:
True
if closed,False
otherwise.
-
Transaction.
commit
()¶ Mark this transaction as successful and close in order to trigger a COMMIT. This is functionally equivalent to:
tx.success = True tx.close()
- Raises
TransactionError – if already closed
-
Transaction.
rollback
()¶ Mark this transaction as unsuccessful and close in order to trigger a ROLLBACK. This is functionally equivalent to:
tx.success = False tx.close()
- Raises
TransactionError – if already closed
-
Closing an explicit transaction can either happen automatically at the end of a with
block, using the Transaction.success
attribute to determine success,
or can be explicitly controlled through the Transaction.commit()
and Transaction.rollback()
methods.
Explicit transactions are most useful for applications that need to distribute Cypher execution across multiple functions for the same transaction.
def create_person(driver, name):
with driver.session() as session:
tx = session.begin_transaction()
node_id = create_person_node(tx)
set_person_name(tx, node_id, name)
tx.commit()
def create_person_node(tx):
return tx.run("CREATE (a:Person)"
"RETURN id(a)", name=name).single().value()
def set_person_name(tx, node_id, name):
tx.run("MATCH (a:Person) WHERE id(a) = $id "
"SET a.name = $name", id=node_id, name=name)
Transaction Functions¶
Transaction functions are the most powerful form of transaction, providing access mode override and retry capabilities. These allow a function object representing the transactional unit of work to be passed as a parameter. This function is called one or more times, within a configurable time limit, until it succeeds. Results should be fully consumed within the function and only aggregate or status values should be returned. Returning a live result object would prevent the driver from correctly managing connections and would break retry guarantees.
def create_person(tx, name):
return tx.run("CREATE (a:Person {name:$name}) "
"RETURN id(a)", name=name).single().value()
with driver.session() as session:
node_id = session.write_transaction(create_person, "Alice")
To exert more control over how a transaction function is carried out, the unit_of_work()
decorator can be used.
-
neo4j.
unit_of_work
(metadata=None, timeout=None)¶ This function is a decorator for transaction functions that allows extra control over how the transaction is carried out.
For example, a timeout (in seconds) may be applied:
@unit_of_work(timeout=25.0) def count_people(tx): return tx.run("MATCH (a:Person) RETURN count(a)").single().value()
Access modes¶
A session can be given a default access mode on construction. This applies only in clustered environments and determines whether transactions carried out within that session should be routed to a read or write server by default.
Note that this mode is simply a default and not a constraint. This means that transaction functions within a session can override the access mode passed to that session on construction.
Note
The driver does not parse Cypher statements and cannot determine whether a statement tagged as read or write is tagged correctly. Since the access mode is not passed to the server, this can allow a write statement to be executed in a read call on a single instance. Clustered environments are not susceptible to this loophole as cluster roles prevent it. This behaviour should not be relied upon as the loophole may be closed in a future release.