Skip to content

Transaction Level #472

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pjebs opened this issue Aug 2, 2016 · 19 comments
Closed

Transaction Level #472

pjebs opened this issue Aug 2, 2016 · 19 comments
Labels

Comments

@pjebs
Copy link

pjebs commented Aug 2, 2016

I was wondering what the best way was to set the transaction level and have it revert back to default after the transaction - particularly in case of a panic:

I'm currently doing this:

    if err := gorm.Exec("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;").Error; err != nil {
        //Handle Error
        return
    }
    tx := gorm.Begin()

...

    //Do something with tx. And call `tx.Rollback()` or `tx.Commit()`
@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

Bear in mind, gorm.Exec(...) just passes raw statement to underlying mysql connection directly and Rollback() / Commit() does the obvious.

@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

No need to do anything special. Solved:
"Without any SESSION or GLOBAL keyword, the statement applies to the next (not started) transaction performed within the current session. Subsequent transactions revert to using the SESSION isolation level. "

@pjebs pjebs closed this as completed Aug 2, 2016
@pjebs pjebs reopened this Aug 2, 2016
@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

Actually, I had to reopen it. I am using mysql-go driver's connection pooling.

How can I guarantee that gorm.Exec(...) command will apply to the very next tx := gorm.Begin() and not to some other connection being run on another goroutine etc.

@methane
Copy link
Member

methane commented Aug 2, 2016

Why don't you use tx.Exec()?
Transaction is strictly bound to connection, of course.

@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

https://dev.mysql.com/doc/refman/5.6/en/set-transaction.html

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.02 sec)

mysql> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
ERROR 1568 (25001): Transaction characteristics can't be changed
while a transaction is in progress

The documentation states it has to be called BEFORE starting transaction.

@methane
Copy link
Member

methane commented Aug 2, 2016

Have you read readme?
https://github.com/go-sql-driver/mysql#system-variables

@methane
Copy link
Member

methane commented Aug 2, 2016

If you want to change variable only for one connection, possible solution is:

  • Use specialized sql.DB for special case.
  • Stop using database/sql. mymysql may help you.

@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

I believe that system variable changes the default transaction level for everything, whereas I only want to change the transaction level for 1 particular query.

Using sql.DB won't help. It will just use underlying mysql driver and it's connection pooling. In fact, I always create a sql.DB and feed it into gorm, without allowing gorm to manage it for me.

I've known of that alternative driver but been hesitant to use it because it doesn't have anywhere near active user base (hence very few updates).

@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

@arnehormann @julienschmidt Any suggestions?

@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

The only solution I can think of is fork this package, and rename the id that database/sql package uses to use the mysql go driver.

That way for this particular query, I can call: Open(driverName, dataSourceName string) (*DB, error) with drivername being "mysql-fork".

A messy hack unfortunately.

@methane
Copy link
Member

methane commented Aug 2, 2016

Using sql.DB won't help. It will just use underlying mysql driver and it's connection pooling. In fact, I always create a sql.DB and feed it into gorm, without allowing gorm to manage it for me.

I meant having two *sql.DB, normal one and specialized one for the query.

@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

Hmmm. I'm quite confused by your suggestion.

If I create a standard one and a specialized one.
The sql.Open("mysql"...) will just use the same mysql driver and that will use it's own shared connection pool that will be shared by both sql.DB objects.

sql1= sql.Open("mysql", "XXX")
sql2= sql.Open("mysql", "XXX?tx_isolation=READ-UNCOMMITTED")

@methane
Copy link
Member

methane commented Aug 2, 2016

No. Each sql.DB has own pool. Connections are not shared between separated pools.

@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

I had no idea. Thanks @methane I'll test it out.

@julienschmidt
Copy link
Member

If you really need it per-connection, a hackish solution might be to use sql.Tx, manually abort the transaction and start a new one.

Otherwise settings can only be made per connection pool / sql.DB instance. The ?tx_isolation=XXX DSN parameter should be the way to go then.

@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

@julienschmidt Can you clarify what you mean by use sql.Tx and then abort?

Is it this?

    tx := gorm.Begin()
if err := tx.Exec("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;").Error; err != nil {
        tx.Rollback()
        return
    }

 tx.Rollback()

//What do I do now?

The documentation states SET TRANSACTION has to be called BEFORE starting transaction (See above comment).

@julienschmidt
Copy link
Member

I don't know the gorm syntax, but it should be something like:

tx := gorm.Begin()
if err := tx.Exec("ROLLBACK").Error; err != nil { ... } // Abort
if err := tx.Exec("START TRANSACTION [options]").Error; err != nil { ... } // Start new transaction with custom options
// Do whatever you wanted to do in the transaction
tx.Commit() // Again, I don't know the syntax, but finish the transaction as usual.

However this is a quite nasty workaround and causes some extra overhead (logging, extra network round-trips etc). If possible, I would use the DSN parameter instead.

@pjebs
Copy link
Author

pjebs commented Aug 2, 2016

thanks @julienschmidt @methane

@mayur-tolexo
Copy link

mayur-tolexo commented Mar 11, 2018

You can try this:

tx, err = dbConn.DB().BeginTx(context.Background(),
			&sql.TxOptions{Isolation: sql.LevelReadUncommitted})

but this will be of *sql.Tx type

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants