Revert "retry on serialization errors"

This commit is contained in:
Stephan Renatus
2018-11-29 08:24:13 +01:00
committed by GitHub
parent f3acec0b1b
commit 8f3cca7ba4
20 changed files with 463 additions and 1355 deletions

View File

@@ -2,15 +2,14 @@
package sql
import (
"context"
"database/sql"
"regexp"
"time"
"github.com/lib/pq"
"github.com/sirupsen/logrus"
// import third party drivers
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
)
@@ -40,66 +39,31 @@ func matchLiteral(s string) *regexp.Regexp {
return regexp.MustCompile(`\b` + regexp.QuoteMeta(s) + `\b`)
}
// Detect a serialization failure, which should trigger retrying the
// transaction according to PostgreSQL docs:
//
// https://www.postgresql.org/docs/current/transaction-iso.html#XACT-SERIALIZABLE
//
// "applications using this level must be prepared to retry transactions due to
// serialization failures"
func isRetryableSerializationFailure(err error) bool {
if pqErr, ok := err.(*pq.Error); ok {
return pqErr.Code.Name() == "serialization_failure"
}
return false
}
var (
// The "github.com/lib/pq" driver is the default flavor. All others are
// translations of this.
flavorPostgres = flavor{
// The default behavior for Postgres transactions is consistent reads, not
// consistent writes. For each transaction opened, ensure it has the
// correct isolation level.
// The default behavior for Postgres transactions is consistent reads, not consistent writes.
// For each transaction opened, ensure it has the correct isolation level.
//
// See: https://www.postgresql.org/docs/9.3/static/sql-set-transaction.html
//
// Be careful not to wrap sql errors in the callback 'fn', otherwise
// serialization failures will not be detected and retried.
// NOTE(ericchiang): For some reason using `SET SESSION CHARACTERISTICS AS TRANSACTION` at a
// session level didn't work for some edge cases. Might be something worth exploring.
executeTx: func(db *sql.DB, fn func(sqlTx *sql.Tx) error) error {
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
opts := &sql.TxOptions{
Isolation: sql.LevelSerializable,
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
for {
tx, err := db.BeginTx(ctx, opts)
if err != nil {
return err
}
if err := fn(tx); err != nil {
if isRetryableSerializationFailure(err) {
continue
}
return err
}
err = tx.Commit()
if err != nil {
if isRetryableSerializationFailure(err) {
continue
}
return err
}
return nil
if _, err := tx.Exec(`SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;`); err != nil {
return err
}
if err := fn(tx); err != nil {
return err
}
return tx.Commit()
},
supportsTimezones: true,