Golang,postgres事务:pq:失败事务中的意外事务状态

时间:2014-09-16 09:03:43

标签: postgresql transactions go libpq

Go:v 1.3 db:postgres使用lib / pq

我有一个更新postgres数据库的应用程序。使用pgbouncer设置postgres数据库。

因此,通过活动连接,我有代码来运行插入和更新。这是插入代码:

func (sitemap *SiteMapData) InsertSiteMap(dbConnection *sql.DB) (int64, error) {

tx, err := dbConnection.Begin()
if err != nil {
    l4g.Error("InsertSiteMap: could not being transaction: %v", err)
    return 0, err
}
result, err := tx.Exec("INSERT INTO sitemap (url) VALUES($1)", sitemap.Url)

if err != nil {
    if !strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
        l4g.Error("sitemapdata.InsertSiteMap: error inserting new sitemap data: %v", err)
    }
    tx.Rollback()
    return 0, nil
}

resultCount, _ := result.RowsAffected()
if err := tx.Commit(); err != nil {
    l4g.Error("InsertSiteMap: could not commit transaction: %v", err)
    return resultCount, err
}
l4g.Info("InsertSiteMap: Insert with %s completed with count: %d", sitemap.Url, resultCount)
return resultCount, nil
}

当我启动应用程序时,这与更新代码一样正常:

func (sitemap *SiteMapData) PersistSiteMapData(dbConnection *sql.DB) (int64, error) {

baseQuery, execType := sitemap.buildUpdateQuery()

// as the number of parameters in the statement may vary the following logic is needed
var result sql.Result
var execErr error

tx, err := dbConnection.Begin()
if err != nil {
    l4g.Error("PersistSiteMap: could not being transaction: %v", err)
    return 0, err
}

switch {
case execType == NoExtraDates:
    result, execErr = tx.Exec(baseQuery, sitemap.ConsecutiveFailCount, sitemap.LastAttempt.Time,
        sitemap.Etag.String, sitemap.InternalChecksum.String, sitemap.Id)
case execType == LastGatheredOnly:
    result, execErr = tx.Exec(baseQuery, sitemap.ConsecutiveFailCount, sitemap.LastAttempt.Time,
        sitemap.Etag.String, sitemap.InternalChecksum.String,
        sitemap.LastGathered.Time, sitemap.Id)
case execType == ModifiedHeaderOnly:
    result, execErr = tx.Exec(baseQuery, sitemap.ConsecutiveFailCount, sitemap.LastAttempt.Time,
        sitemap.Etag.String, sitemap.InternalChecksum.String,
        sitemap.ModifiedHeader.Time, sitemap.Id)
case execType == BothDates:
    result, execErr = tx.Exec(baseQuery, sitemap.ConsecutiveFailCount, sitemap.LastAttempt.Time,
        sitemap.Etag.String, sitemap.InternalChecksum.String,
        sitemap.LastGathered.Time, sitemap.ModifiedHeader.Time, sitemap.Id)
}

if execErr != nil {
    tx.Rollback()
    return -1, fmt.Errorf("PersistSiteMapData Error %s: %v", baseQuery, execErr)
}

resultCount, _ := result.RowsAffected()
if err := tx.Commit(); err != nil {
    l4g.Error("PersistSiteMap: could not commit transaction: %v", err)
    return resultCount, err
}
l4g.Info("PersistSiteMapData Updated sitemap %s(%d) correctly", sitemap.Url, sitemap.Id)
return resultCount, nil
}

// buildUpdateQuery returns the update query dependent on the presence of valid datetime fields.
func (sitemap *SiteMapData) buildUpdateQuery() (string, int) {
// note: lastAttempt is not covered here as this is set immediatley prior to the retrieval attempt
nextParam := 5
execType := NoExtraDates
baseQuery := "UPDATE sitemap " +
    "SET " +
    "consecutive_fail_count = $1, last_attempt = $2, etag = $3, internal_checksum = $4"

if sitemap.LastGathered.Valid {
    baseQuery = fmt.Sprintf("%s, last_gathered = $%d", baseQuery, nextParam)
    nextParam++
    execType += LastGatheredOnly
}

if sitemap.ModifiedHeader.Valid {
    baseQuery = fmt.Sprintf("%s, modified_header = $%d", baseQuery, nextParam)
    nextParam++
    execType += ModifiedHeaderOnly
}
baseQuery = fmt.Sprintf("%s WHERE id = $%d", baseQuery, nextParam)

return baseQuery, execType
}

日志显示更新首先正确发生,然后我在一段时间后再次检查日志,我看到了:

pq: unexpected transaction status in a failed transaction

通过lib / pq代码跟踪,看起来这是在调用* sql.DB.Begin()并且事务已在运行时发生的。

我想知道是否有人可以对此有所了解?

我有点想,也许我应该专门跟踪该消息,如果我得到它进入基于时间的重试循环?或者有没有办法找到错误的交易并将其删除?

由于 森

1 个答案:

答案 0 :(得分:1)

来自http://golang.org/pkg/database/sql/#DB.Begin

  

开始开始交易。隔离级别取决于驱动程序。

所以看来lib / pq并不能很好地处理事务的隔离。

唯一的解决方法是使用sync.Mutex在本地锁定事务并在其问题跟踪器上提交错误,因为这是驱动程序错误。