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))。