Go: v 1.3 db: postgres с использованием lib/pq
У меня есть приложение, которое обновляет базу данных postgres. База данных postgres настроена с помощью pgbouncer.
Итак, при активном соединении у меня есть код для запуска вставок и обновлений. Вот код вставки:
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() и транзакция уже выполняется.
Интересно, может ли кто-нибудь пролить больше света на это?
Я как бы думаю, что, возможно, мне следует специально отслеживать это сообщение, и если я получу его, войдите в цикл повторных попыток, основанный на времени? Или есть способ найти ошибочную транзакцию и убить ее?
Спасибо, Натан.