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 same Session 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, multiple Session 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 and dict. Note however that list 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:

success __exit__ cleanly __exit__ with exception tx.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.