Golang - pgbouncer 和事务使用

nat*_*j07 5 postgresql go pgbouncer

技术细节

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

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

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

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

对于 select 语句,我将其包装在事务中:

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
    }
Run Code Online (Sandbox Code Playgroud)

(嗯嗯,这似乎稍微取消了缩进)AsS,你可以看到我正在开始但没有关闭交易。这会导致 pg 方面出现问题,其中每个选择都处于“事务中空闲”状态。

我尝试过 tx.Commit() 和 tx.Rollback() ,在这两种情况下我都会收到错误:

"unknown response for simple query '3'"
Run Code Online (Sandbox Code Playgroud)

或者

"unknown response for simple query 'D'"
Run Code Online (Sandbox Code Playgroud)

如何在 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
}
Run Code Online (Sandbox Code Playgroud)

我在日志中看到的代码如下:

pq: unexpected describe rows response: '3'
Run Code Online (Sandbox Code Playgroud)

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

nat*_*j07 4

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

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

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

谢谢内森

  • 当您“提交”事务时,您是在告诉 pgbouncer 它可以为另一个事务回收后端。`BEGIN`,查询,读取行,`COMMIT`/`ROLLBACK`。 (2认同)