cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Transactions and auto-commit

Hi everyone!
I am using go-client and i can't understand one thing - should I commit or rollback transactions manually?
WriteTransaction and ReadTransaction are used in such way:
 

 

 

session := r.driver.NewSession(neo4j.SessionConfig{
		DatabaseName: r.dataBaseName,
		AccessMode:   neo4j.AccessModeWrite,
	})
	defer session.Close()

	result, err := session.WriteTransaction(func(tx neo4j.Transaction) (interface{}, error) {
		result, err := tx.Run(q, params)

		if err != nil {
			return nil, err
		}

		record, err := result.Single()
		if err != nil {
			return nil, err
		}

		//@TODO tx.Commit() // ??
		//@TODO tx.Rollback() // ??

		node := record.Values[0].(dbtype.Node)
		return r.nodeToEntity(node), nil
	})

 

 

None of the documentation emphasizes the need to commit or rollback a transaction. In addition, in the documentation, I found a mention of operations with auto-commit behavior. Do I understand correctly that ReadTransaction (or WriteTransaction) is automatically commited, especially if only one request is executed inside it?

1 ACCEPTED SOLUTION

Hello,

I agree this is confusing and the good news is: the confusion will be gone in 5.0 *.

When you are using session.WriteTransaction or session.ReadTransaction, the callback you provide (which we call a transaction function), receives a special kind of transaction: a managed transaction.

Since the transaction is managed internally, you must not call Commit, Rollback or Close yourself. In fact, if you do, you will systematically get an error. The driver will call these itself when appropriate.

How is the confusion gone in 5.0? The Transaction interface has been split into:

  • ExplicitTransaction (with Commit/Rollback/Close): this is created when you call session.BeginTransaction. Here you must call Commit or Rollback/Close.
  • ManagedTransaction (without Commit/Rollback/Close): this is passed to the callback you provide to session.ReadTransaction and session.WriteTransaction. Now, the compiler won't even let you call Commit, Rollback or Close.

* Let me amend my initial statement a little bit. The confusion is not entirely gone in 5.0. In fact, the existing Transaction interface is still there but is deprecated. The main motivation for keeping the old interface around is to not complicate the migration path from 4.x to 5.0. The ExplicitTransaction and ManagedTransaction interfaces are exposed only when you use the new driver APIs that are context-aware (they had been requested for quite some time).

So far, we've talked about transactions that are initiated from the driver side. They're created via session.BeginTransaction and session.ReadTransaction/session.WriteTransaction.

There is another kind: auto-commit transactions. One may think this has to do with the automatic management of commits/rollbacks of transaction functions, but it is not. Auto-commit transactions are transactions that are created and managed entirely on the server side. This happens only when you call session.Run.

Some specific Cypher queries do not work with driver transactions, these are:

These must be executed with session.Run. These transactions managed on the server side are what we refer to as auto-commit transactions.

Somewhat surprisingly, auto-commit Cypher extensions (such as apoc.periodic.commit) can run in explicit transactions, but that's not a good idea.

Why?
Because auto-commit workloads spawn new transactions on their own. Rolling back the explicit transaction would have no effect onto the spawned, independent transactions. Moreover, if you ran more other queries in an explicit transaction, the transaction status report can get confusing: the explicit transaction could be committed (i.e. successful) even though some of the spawned transactions are not.

This leaves us with one final question: why not use session.Run all the time?

There are a few circumstances where session.Run is not a good fit:

  • you want to execute several queries within a single transaction (session.BeginTransaction is the right fit here)
  • you want transactions to be automatically retried when a transient error occurs, such as a database cluster leader switch e.g. (session.ReadTransaction/session.WriteTransaction are the ones to use here - the callback you provide may be called several times until the transient error does not occur anymore, or a non-retryable error occurs or you ran out of retry attempts)

In 5.0, you'll get the best of both worlds as a new reliable option: explicit transaction management with BeginTransaction and custom retry logic if you want to (an API to determine whether errors are safe to retry will be made publicly available).

This is A LOT to consider, isn't it? We're working hard to trying to simplify the API surface of the drivers and we'll hopefully be able to make your lives easier in the 5.x series.

View solution in original post

2 REPLIES 2

Hello,

I agree this is confusing and the good news is: the confusion will be gone in 5.0 *.

When you are using session.WriteTransaction or session.ReadTransaction, the callback you provide (which we call a transaction function), receives a special kind of transaction: a managed transaction.

Since the transaction is managed internally, you must not call Commit, Rollback or Close yourself. In fact, if you do, you will systematically get an error. The driver will call these itself when appropriate.

How is the confusion gone in 5.0? The Transaction interface has been split into:

  • ExplicitTransaction (with Commit/Rollback/Close): this is created when you call session.BeginTransaction. Here you must call Commit or Rollback/Close.
  • ManagedTransaction (without Commit/Rollback/Close): this is passed to the callback you provide to session.ReadTransaction and session.WriteTransaction. Now, the compiler won't even let you call Commit, Rollback or Close.

* Let me amend my initial statement a little bit. The confusion is not entirely gone in 5.0. In fact, the existing Transaction interface is still there but is deprecated. The main motivation for keeping the old interface around is to not complicate the migration path from 4.x to 5.0. The ExplicitTransaction and ManagedTransaction interfaces are exposed only when you use the new driver APIs that are context-aware (they had been requested for quite some time).

So far, we've talked about transactions that are initiated from the driver side. They're created via session.BeginTransaction and session.ReadTransaction/session.WriteTransaction.

There is another kind: auto-commit transactions. One may think this has to do with the automatic management of commits/rollbacks of transaction functions, but it is not. Auto-commit transactions are transactions that are created and managed entirely on the server side. This happens only when you call session.Run.

Some specific Cypher queries do not work with driver transactions, these are:

These must be executed with session.Run. These transactions managed on the server side are what we refer to as auto-commit transactions.

Somewhat surprisingly, auto-commit Cypher extensions (such as apoc.periodic.commit) can run in explicit transactions, but that's not a good idea.

Why?
Because auto-commit workloads spawn new transactions on their own. Rolling back the explicit transaction would have no effect onto the spawned, independent transactions. Moreover, if you ran more other queries in an explicit transaction, the transaction status report can get confusing: the explicit transaction could be committed (i.e. successful) even though some of the spawned transactions are not.

This leaves us with one final question: why not use session.Run all the time?

There are a few circumstances where session.Run is not a good fit:

  • you want to execute several queries within a single transaction (session.BeginTransaction is the right fit here)
  • you want transactions to be automatically retried when a transient error occurs, such as a database cluster leader switch e.g. (session.ReadTransaction/session.WriteTransaction are the ones to use here - the callback you provide may be called several times until the transient error does not occur anymore, or a non-retryable error occurs or you ran out of retry attempts)

In 5.0, you'll get the best of both worlds as a new reliable option: explicit transaction management with BeginTransaction and custom retry logic if you want to (an API to determine whether errors are safe to retry will be made publicly available).

This is A LOT to consider, isn't it? We're working hard to trying to simplify the API surface of the drivers and we'll hopefully be able to make your lives easier in the 5.x series.

TO DELETE: initial reply now edited