什么是在事务中途提交工作的简单方法,但随后继续

时间:2017-07-01 07:36:46

标签: sql database postgresql go transactions

背景

我正在使用带有Postgres数据库的github.com/jmoiron/sqlx golang包。

我有以下包装函数在事务中运行SQL代码:

func (s *postgresStore) runInTransaction(ctx context.Context, fn func(*sqlx.Tx) error) error {
    tx, err := s.db.Beginx()
    if err != nil {
        return err
    }
    defer func() {
        if err != nil {
            tx.Rollback()
            return
        }
        err = tx.Commit()
    }()
    err = fn(tx)
    return err
}

鉴于此,请考虑以下代码:

func (s *store) SampleFunc(ctx context.Context) error {
    err := s.runInTransaction(ctx,func(tx *sqlx.Tx) error {

        // Point A: Do some database work

        if err := tx.Commit(); err != nil {
            return err
        }

        // Point B: Do some more database work, which may return an error
    })
}

期望的行为

  • 如果A点出现错误,那么交易应该没有工作
  • 如果B点出现错误,那么该交易仍应完成A点的工作。

当前代码的问题

目前代码无法正常工作,因为我提交了两次交易(一次在runInTransaction,一次在SampleFunc)。

可能的解决方案

在我提交交易的地方,我可以改为运行tx.Exec("SAVEPOINT my_savepoint"),然后defer tx.Exec("ROLLBACK TO SAVEPOINT my_savepoint")

在B点的代码之后,我可以运行:tx.Exec("RELEASE SAVEPOINT my_savepoint")

因此,如果B点的代码运行没有错误,我将无法ROLLBACK到我的保存点。

可能解决方案的问题

我不确定使用保存点是否会破坏数据库/ sql包的行为。此外,我的解决方案似乎有点凌乱 - 当然有更简洁的方法来做到这一点!

2 个答案:

答案 0 :(得分:2)

多笔交易

您可以在两个交易中分割您的工作:

func (s *store) SampleFunc(ctx context.Context) error {
    err := s.runInTransaction(ctx,func(tx *sqlx.Tx) error {
        // Point A: Do some database work
    })
    if err != nil {
        return err
    }
    return s.runInTransaction(ctx,func(tx *sqlx.Tx) error {
        // Point B: Do some more database work, which may return an error
    })
}

答案 1 :(得分:1)

我有同样的问题:我在一次交易中有很多步骤。 开始交易后:

  • BEGIN
  • 循环:

    • SAVEPOINT s1
    • 一些行动....
    • 如果收到错误:ROLLBACK TO SAVEPOINT s1
    • 如果确定转到下一步
  • 最后COMMIT

这种方法使我能够逐个执行所有步骤。如果某些步骤失败,我可以只丢弃他们,保留其他人。并最终承诺所有"好"工作