DATA-DOG / go-txdb

Immutable transaction isolated sql driver for golang

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is it possible to run a transaction within txdb?

qdm12 opened this issue · comments

Hello there,

I'm trying to use your driver together with the underlying mysql driver, a mysql database and sqlx. Unfortunately whenever I create a transaction in my production code, it fails tx.Commit() or tx.Rollback(), usually with the error saying that the save point does not exist for tx_0 for example. Is it possible to run transactions within a (hidden) transaction started by your txdb driver?

Thanks!

Hi, it is possible and it uses save points for transactions. There a test case and it works well for mysql and postgres at least. If this does not work in sqlx it means it uses a different sql.DB instance opened. Are you sure that it uses the same db and not the other? Maybe you have a portion of code which opens different db instances?

This works well for many txdb users and this is a common use case, I’m confident that it works as expected, the only reasonable issue is that you have a differnt db instancd there created and probably the code you have is not properly implemented in the first place.

If you want any further help, produce a minimum test case example within 50 lines of code using sqlx and txdb as a tiny scenario model you have failing. Since I’m not sure what sqlx may be doing behind the scene

Hello thanks for the quick reply!

Running mysql with

docker run -it --rm -p 3306:3306/tcp -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -e MYSQL_DATABASE=test mysql:8.0

With this Go code

package main

import (
	"context"
	"log"
	"os"

	"github.com/DATA-DOG/go-txdb"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

func init() {
	txdb.Register("txdb", "mysql", "root@tcp(localhost:3306)/test")
}

func main() {
	ctx := context.Background()
	os.Exit(_main(ctx))
}

func _main(ctx context.Context) int {
	db, err := sqlx.Open("txdb", "identifier")
	if err != nil {
		log.Println(err)
		return 1
	}
	defer db.Close()

	tx, err := db.BeginTx(ctx, nil)
	if err != nil {
		log.Println(err)
		return 1
	}
	_, err = tx.Exec(`CREATE TABLE IF NOT EXISTS test (id INT)`)
	if err != nil {
		log.Println(err)
		return 1
	}
	err = tx.Commit()
	if err != nil {
		log.Println(err)
		return 1
	}
	return 0
}

Does fail with Error 1305: SAVEPOINT tx_1 does not exist from tx.Commit(). I also tried with db.Begin() with the same outcome. Running it without txdb works though.

Thanks!

Also I just tried replacing sqlx with database/sql and it doesn't worked either. So the issue is likely unrelated with sqlx? 🤔 Thanks for looking into this.

It may be the case, that begin transaction with context is not implemented in the interface. Will have a look the next day, thanks for the details

looked for some time today, there seems to be a bug in savepoint logic, made some progress, but will continue on it the another day

Thanks! And amazing package by the way, I hope we can get it fixed. I also tried with db.Begin() without success. Let me know if I can help in any way.

Thanks, cannot figure this out yet, and sadly do not have that much time to spend daily. but there is certainly something not right with the Savepoints. if you have some time to contribute, it would be great if you could give a shot to determine the issue. the code part related to save points is here the logic is to create a SAVEPOINT within transaction and store the savepoint id for rollback or commit and the error comes from the Commit part, for some reason it does not find the savepoint by the id or maybe it does not even start one, probably would be best to enable query log to see what happens there. Or it may use a different connection for some reason, but very unlikely.

I think all the issues are related to the exec statement:

_, err = tx.Exec(`CREATE TABLE IF NOT EXISTS test (id INT)`)
	if err != nil {
		log.Println(err)
		return 1
	}

Mysql does not support rolling back any table modifications. postgres on the other hand does support these in transactions.
So the mysql behavior is different when you create/alter table inside transaction. it no longer supports any savepoints or rollbacks. this is how mysql works in general. you can read about it more in here.

See here:
2020-12-20-203415

Now if we just do the usual non table modifications, it works as expexted:
2020-12-20-203754

So txdb is working as expected and the problem is in database or the way you use it.
I personally prefer using postgres

I was surprised why the tests were not failing, since I had it for mysql and postgres with the savepoints, it was just that it was not mutating tables

Indeed, now it works. I prefer Postgres too, MySql feels worst in many ways. But I'm stuck with it for various compatibility reasons unfortunately 😉 Anyway thanks for your time digging into this. I'm now happily using go-txdb instead of creating a database per test 🤣

Here is an additional information about this issue.
I have confirmed that the same problem occurs with the UPDATE statement in mysql.
As this issue concludes, it is preferable to use postgresql.