Golang - 使用pgbouncer和事务处理

5

技术细节

  • go版本1.2
  • 使用go语言库bmizerany/pq访问postgres数据库

这个问题让我非常疑惑,我希望有人能够帮助我。

我开发了一个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
    }

(嗯,这似乎有点打乱缩进)如您所见,我开始了事务但未关闭它。这会导致在pg方面的问题,其中每个select都处于“空闲事务”状态。

我尝试了tx.Commit()和tx.Rollback(),但两种情况都出现了错误:

"unknown response for simple query '3'"

或者
"unknown response for simple query 'D'"

我希望能够成功关闭Go中的交易。我希望更新我们的pgbouncer.ini文件以允许我切换到lib/pq驱动程序库,但我不确定这是否会直接解决此问题。
那么,我应该如何正确关闭tx对象,还是有办法强制Go在幕后不使用预处理语句?
谢谢 Nathan
我已经尝试过做出一些改变:
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'

但是,我应该指出的是,在尝试第二次选择语句时出现这种情况。此应用程序选择一批数据进行处理,然后选择随后的一批数据。这个错误发生在第二个选择过程中。第一个选择没有任何问题。


Postgres日志中有什么内容吗?有问题的语句可能会被记录在那里,这将允许您查看您认为发送的内容是否实际上被数据库接收。 - Dmitri Goldring
我需要向DBA团队询问,因为我没有访问那些日志的权限。有趣的是,如果我保留事务封闭,则应用程序按预期运行,但会使pgbouncer处于空闲事务状态下。 - nathj07
2个回答

4

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

我之前的代码是将行对象返回给上面的调用代码(未显示)。在读取行之前关闭事务似乎是问题的原因。如果您理解得更深入,请澄清一下。

现在我同时返回行和事务对象。当我读取完所有行后,我回滚事务。这样可以保持一切正常运作。

谢谢 Nathan


2
当您执行COMMIT事务时,您告诉pgbouncer可以回收后端以供另一个事务使用。BEGIN,查询,读取行,COMMIT/ROLLBACK - Sean

1

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

这就是为什么一旦事务关闭,您就无法再迭代游标的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接