pg-go RunInTransaction 不回滚事务
pg-go RunInTransaction not rolling back the transaction
我正在尝试在场景之间回滚我的单元测试的事务,以保持数据库为空并且不使我的测试变脏。所以,我正在尝试:
for _, test := range tests {
db := connect()
_ = db.RunInTransaction(func() error {
t.Run(test.name, func(t *testing.T) {
for _, r := range test.objToAdd {
err := db.PutObj(&r)
require.NoError(t, err)
}
objReturned, err := db.GetObjsWithFieldEqualsXPTO()
require.NoError(t, err)
require.Equal(t, test.queryResultSize, len(objReturned))
})
return fmt.Errorf("returning error to clean up the database rolling back the transaction")
})
}
我希望在场景结束时回滚事务,所以下一步将有一个空数据库,但是当我运行时,数据永远不会回滚。
我相信我正在尝试按照文档的建议进行操作:https://pg.uptrace.dev/faq/#how-to-test-mock-database,对吗?
更多信息:我注意到我的界面在 RunInTransaction 上实现了一个层:
func (gs *DB) RunInTransaction(fn func() error) error {
f := func(*pg.Tx) error { return fn() }
return gs.pgDB.RunInTransaction(f)
}
IDK 问题是什么,但我真的猜想这与此有关(因为 TX 被封装在 RunInTransaction 实现中。
go-pg uses connection pooling (in common with most go database packages). This means that when you call a database function (e.g. db.Exec
) 它将从池中获取一个连接(如果需要则建立一个新连接),运行 命令和 return 到池的连接。
当运行执行运行操作时,您需要运行 BEGIN
, whatever updates etc you require, followed by COMMIT
/ROLLBACK
, on a single connection dedicated to the transaction (any commands sent on other connections are not part of the transaction). This is why Begin() (and effectively RunInTransaction
) provide you with a pg.Tx
;在 t运行saction.
中将其用于 运行 命令
example_test.go provides an example covering the usage of RunInTransaction
:
incrInTx := func(db *pg.DB) error {
// Transaction is automatically rollbacked on error.
return db.RunInTransaction(func(tx *pg.Tx) error {
var counter int
_, err := tx.QueryOne(
pg.Scan(&counter), `SELECT counter FROM tx_test FOR UPDATE`)
if err != nil {
return err
}
counter++
_, err = tx.Exec(`UPDATE tx_test SET counter = ?`, counter)
return err
})
}
您会注意到,这仅在调用 RunInTransaction
时使用 pg.DB
;所有数据库操作都使用 t运行saction tx
(a pg.Tx)。 tx.QueryOne
将在 t运行 行动中成为 运行;如果你 运行 db.QueryOne
那么那将是 运行 在 t运行 行动之外。
所以 RunInTransaction
begins a transaction and passes the relevant Tx 作为您提供的函数的参数。您将其包装为:
func (gs *DB) RunInTransaction(fn func() error) error {
f := func(*pg.Tx) error { return fn() }
return gs.pgDB.RunInTransaction(f)
}
这实际上忽略了 pg.Tx
,然后您使用其他连接(例如 err := db.PutObj(&r)
)(即在 t运行 操作之外)执行 运行 命令。要解决此问题,您需要使用 t运行saction(例如 err := tx.PutObj(&r)
)。
我正在尝试在场景之间回滚我的单元测试的事务,以保持数据库为空并且不使我的测试变脏。所以,我正在尝试:
for _, test := range tests {
db := connect()
_ = db.RunInTransaction(func() error {
t.Run(test.name, func(t *testing.T) {
for _, r := range test.objToAdd {
err := db.PutObj(&r)
require.NoError(t, err)
}
objReturned, err := db.GetObjsWithFieldEqualsXPTO()
require.NoError(t, err)
require.Equal(t, test.queryResultSize, len(objReturned))
})
return fmt.Errorf("returning error to clean up the database rolling back the transaction")
})
}
我希望在场景结束时回滚事务,所以下一步将有一个空数据库,但是当我运行时,数据永远不会回滚。
我相信我正在尝试按照文档的建议进行操作:https://pg.uptrace.dev/faq/#how-to-test-mock-database,对吗?
更多信息:我注意到我的界面在 RunInTransaction 上实现了一个层:
func (gs *DB) RunInTransaction(fn func() error) error {
f := func(*pg.Tx) error { return fn() }
return gs.pgDB.RunInTransaction(f)
}
IDK 问题是什么,但我真的猜想这与此有关(因为 TX 被封装在 RunInTransaction 实现中。
go-pg uses connection pooling (in common with most go database packages). This means that when you call a database function (e.g. db.Exec
) 它将从池中获取一个连接(如果需要则建立一个新连接),运行 命令和 return 到池的连接。
当运行执行运行操作时,您需要运行 BEGIN
, whatever updates etc you require, followed by COMMIT
/ROLLBACK
, on a single connection dedicated to the transaction (any commands sent on other connections are not part of the transaction). This is why Begin() (and effectively RunInTransaction
) provide you with a pg.Tx
;在 t运行saction.
example_test.go provides an example covering the usage of RunInTransaction
:
incrInTx := func(db *pg.DB) error {
// Transaction is automatically rollbacked on error.
return db.RunInTransaction(func(tx *pg.Tx) error {
var counter int
_, err := tx.QueryOne(
pg.Scan(&counter), `SELECT counter FROM tx_test FOR UPDATE`)
if err != nil {
return err
}
counter++
_, err = tx.Exec(`UPDATE tx_test SET counter = ?`, counter)
return err
})
}
您会注意到,这仅在调用 RunInTransaction
时使用 pg.DB
;所有数据库操作都使用 t运行saction tx
(a pg.Tx)。 tx.QueryOne
将在 t运行 行动中成为 运行;如果你 运行 db.QueryOne
那么那将是 运行 在 t运行 行动之外。
所以 RunInTransaction
begins a transaction and passes the relevant Tx 作为您提供的函数的参数。您将其包装为:
func (gs *DB) RunInTransaction(fn func() error) error {
f := func(*pg.Tx) error { return fn() }
return gs.pgDB.RunInTransaction(f)
}
这实际上忽略了 pg.Tx
,然后您使用其他连接(例如 err := db.PutObj(&r)
)(即在 t运行 操作之外)执行 运行 命令。要解决此问题,您需要使用 t运行saction(例如 err := tx.PutObj(&r)
)。