4

技术细节

  • 去 1.2 版
  • 用于 go bmizerany/pq 的 postrgres 库

这个问题让我发疯,我希望有人能够提供帮助。

我在 golang 中开发了一个应用程序来从 postgres 数据库中读取数据,并为每条记录发出一个 http 请求,然后更新数据库。

这一切都很简单。但是,我们有 pgbouncer。我们对 pgbouncer 的配置是这样的,它不支持准备好的语句。Go 默默地将所有查询包装在一个准备好的语句中。pgbouncer 的解决方法是设置一个事务。这对于诸如插入/更新/删除之类的事情都很好。

在选择语句的情况下,我将其包装在事务中:

func TransactionQuery(db *sql.DB, baseQuery string) (rows *sql.Rows, code int, err error) {
        tx, txErr := db.Begin()
        if txErr != nil {
            return nil, -1, txErr
        }

        selectStmt, prepErr := tx.Prepare(baseQuery)
        if prepErr != nil {
            return nil, -1, fmt.Errorf("Failed to prepare statment: %s Error: %v", baseQuery, prepErr)
        }

        defer func() {
            if stmtErr := selectStmt.Close(); stmtErr != nil {
                rows = nil
                code = -2
                err = fmt.Errorf("Failed to close statement: %v.", stmtErr)
            }
        }()

        rows, err = selectStmt.Query()
        if err != nil {
            fmt.Errorf("Failed to retrieve data: %v", err)
            return nil, -1, err
        }
        return rows, 0, nil
    }

(嗯,这似乎有点让缩进消失了) AsS 你可以看到我开始 bnut 没有关闭交易。这会导致 pg 方面的问题,即每个选择都处于“事务中的空闲”状态。

我已经尝试过 tx.Commit() 和 tx.Rollback() 并且在这两种情况下我都会遇到错误:

"unknown response for simple query '3'"

或者

"unknown response for simple query 'D'"

如何在 Go 中成功关闭交易?我希望更新我们的 pgbouncer.ini 以允许我切换到驱动程序库的 lib/pq,但我不确定这是否会直接帮助解决这个问题。

那么,如何正确关闭 tx 对象,或者有没有办法强制 Go 在后台不使用准备好的语句?

谢谢内森

我试图改变一些事情:

func TransactionQuery(db *sql.DB, baseQuery string) (rows *sql.Rows, code int, err error) {
    tx, txErr := db.Begin()
    if txErr != nil {
        return nil, -1, txErr
    }

    /*selectStmt, prepErr := tx.Prepare(baseQuery)
      if prepErr != nil {
          return nil, -1, fmt.Errorf("Failed to prepare statment: %s Error: %v", baseQuery, prepErr)
      }
    */
    rows, err = tx.Query(baseQuery)
    if err != nil {
        fmt.Errorf("Failed to retrieve data: %v", err)
        return nil, -1, err
    }

    /*    if stmtErr := selectStmt.Close(); stmtErr != nil {
          rows = nil
          code = -2
          err = fmt.Errorf("Failed to close statement: %v.", stmtErr)
      }*/

    if txCloseErr := tx.Commit(); txErr != nil {
        rows = rows
        code = -3
        err = txCloseErr
    }
    return rows, 0, nil
}

我在日志中看到的这段代码:

pq: unexpected describe rows response: '3'

但是,我应该指出,这是第二次尝试选择语句时。此应用程序选择一个批次,处理它,然后选择一个后续批次。此错误发生在第二次选择上。第一个选择没有问题。

4

2 回答 2

3

对于遇到这个问题的其他人,我已经解决了这个问题。

我的代码是将 rows 对象返回给上面的调用代码(未显示)。在读取行之前关闭事务似乎是问题的原因。我不完全明白为什么,如果你这样做,请澄清。

现在我返回行和事务对象。然后,当我读取所有行时,我回滚事务。这使一切正常。

谢谢内森

于 2014-04-11T16:06:55.533 回答
0

Tx.Query 返回一个 sql.Rows 的实例,它是一个指向结果集的游标。在您迭代此游标时,不一定检索到数据。在迭代游标时,事务必须保持打开状态。

这就是为什么一旦事务关闭就不能再迭代游标的原因。

于 2018-01-23T16:38:18.833 回答