mirror of
https://github.com/golang/go
synced 2024-11-18 14:04:45 -07:00
database/sql: simplify retry logic when the connection is bad
Simplify retry logic when got bad connection
Change-Id: I92494c6c020576ec01bc4868334ee920ded7aa57
GitHub-Last-Rev: 7499b0c941
GitHub-Pull-Request: golang/go#54043
Reviewed-on: https://go-review.googlesource.com/c/go/+/419182
Reviewed-by: Daniel Theophanes <kardianos@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
Run-TryBot: hopehook <hopehook@golangcn.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
88149ed43e
commit
0752f4a12c
@ -846,17 +846,12 @@ func (db *DB) pingDC(ctx context.Context, dc *driverConn, release func(error)) e
|
|||||||
func (db *DB) PingContext(ctx context.Context) error {
|
func (db *DB) PingContext(ctx context.Context) error {
|
||||||
var dc *driverConn
|
var dc *driverConn
|
||||||
var err error
|
var err error
|
||||||
var isBadConn bool
|
|
||||||
for i := 0; i < maxBadConnRetries; i++ {
|
err = db.retry(func(strategy connReuseStrategy) error {
|
||||||
dc, err = db.conn(ctx, cachedOrNewConn)
|
dc, err = db.conn(ctx, strategy)
|
||||||
isBadConn = errors.Is(err, driver.ErrBadConn)
|
return err
|
||||||
if !isBadConn {
|
})
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isBadConn {
|
|
||||||
dc, err = db.conn(ctx, alwaysNewConn)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1539,6 +1534,18 @@ func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
|
|||||||
// connection to be opened.
|
// connection to be opened.
|
||||||
const maxBadConnRetries = 2
|
const maxBadConnRetries = 2
|
||||||
|
|
||||||
|
func (db *DB) retry(fn func(strategy connReuseStrategy) error) error {
|
||||||
|
for i := int64(0); i < maxBadConnRetries; i++ {
|
||||||
|
err := fn(cachedOrNewConn)
|
||||||
|
// retry if err is driver.ErrBadConn
|
||||||
|
if err == nil || !errors.Is(err, driver.ErrBadConn) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(alwaysNewConn)
|
||||||
|
}
|
||||||
|
|
||||||
// PrepareContext creates a prepared statement for later queries or executions.
|
// PrepareContext creates a prepared statement for later queries or executions.
|
||||||
// Multiple queries or executions may be run concurrently from the
|
// Multiple queries or executions may be run concurrently from the
|
||||||
// returned statement.
|
// returned statement.
|
||||||
@ -1550,17 +1557,12 @@ const maxBadConnRetries = 2
|
|||||||
func (db *DB) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
|
func (db *DB) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
|
||||||
var stmt *Stmt
|
var stmt *Stmt
|
||||||
var err error
|
var err error
|
||||||
var isBadConn bool
|
|
||||||
for i := 0; i < maxBadConnRetries; i++ {
|
err = db.retry(func(strategy connReuseStrategy) error {
|
||||||
stmt, err = db.prepare(ctx, query, cachedOrNewConn)
|
stmt, err = db.prepare(ctx, query, strategy)
|
||||||
isBadConn = errors.Is(err, driver.ErrBadConn)
|
return err
|
||||||
if !isBadConn {
|
})
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isBadConn {
|
|
||||||
return db.prepare(ctx, query, alwaysNewConn)
|
|
||||||
}
|
|
||||||
return stmt, err
|
return stmt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1628,17 +1630,12 @@ func (db *DB) prepareDC(ctx context.Context, dc *driverConn, release func(error)
|
|||||||
func (db *DB) ExecContext(ctx context.Context, query string, args ...any) (Result, error) {
|
func (db *DB) ExecContext(ctx context.Context, query string, args ...any) (Result, error) {
|
||||||
var res Result
|
var res Result
|
||||||
var err error
|
var err error
|
||||||
var isBadConn bool
|
|
||||||
for i := 0; i < maxBadConnRetries; i++ {
|
err = db.retry(func(strategy connReuseStrategy) error {
|
||||||
res, err = db.exec(ctx, query, args, cachedOrNewConn)
|
res, err = db.exec(ctx, query, args, strategy)
|
||||||
isBadConn = errors.Is(err, driver.ErrBadConn)
|
return err
|
||||||
if !isBadConn {
|
})
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isBadConn {
|
|
||||||
return db.exec(ctx, query, args, alwaysNewConn)
|
|
||||||
}
|
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1703,17 +1700,12 @@ func (db *DB) execDC(ctx context.Context, dc *driverConn, release func(error), q
|
|||||||
func (db *DB) QueryContext(ctx context.Context, query string, args ...any) (*Rows, error) {
|
func (db *DB) QueryContext(ctx context.Context, query string, args ...any) (*Rows, error) {
|
||||||
var rows *Rows
|
var rows *Rows
|
||||||
var err error
|
var err error
|
||||||
var isBadConn bool
|
|
||||||
for i := 0; i < maxBadConnRetries; i++ {
|
err = db.retry(func(strategy connReuseStrategy) error {
|
||||||
rows, err = db.query(ctx, query, args, cachedOrNewConn)
|
rows, err = db.query(ctx, query, args, strategy)
|
||||||
isBadConn = errors.Is(err, driver.ErrBadConn)
|
return err
|
||||||
if !isBadConn {
|
})
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isBadConn {
|
|
||||||
return db.query(ctx, query, args, alwaysNewConn)
|
|
||||||
}
|
|
||||||
return rows, err
|
return rows, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1840,17 +1832,12 @@ func (db *DB) QueryRow(query string, args ...any) *Row {
|
|||||||
func (db *DB) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error) {
|
func (db *DB) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error) {
|
||||||
var tx *Tx
|
var tx *Tx
|
||||||
var err error
|
var err error
|
||||||
var isBadConn bool
|
|
||||||
for i := 0; i < maxBadConnRetries; i++ {
|
err = db.retry(func(strategy connReuseStrategy) error {
|
||||||
tx, err = db.begin(ctx, opts, cachedOrNewConn)
|
tx, err = db.begin(ctx, opts, strategy)
|
||||||
isBadConn = errors.Is(err, driver.ErrBadConn)
|
return err
|
||||||
if !isBadConn {
|
})
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isBadConn {
|
|
||||||
return db.begin(ctx, opts, alwaysNewConn)
|
|
||||||
}
|
|
||||||
return tx, err
|
return tx, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1921,17 +1908,12 @@ var ErrConnDone = errors.New("sql: connection is already closed")
|
|||||||
func (db *DB) Conn(ctx context.Context) (*Conn, error) {
|
func (db *DB) Conn(ctx context.Context) (*Conn, error) {
|
||||||
var dc *driverConn
|
var dc *driverConn
|
||||||
var err error
|
var err error
|
||||||
var isBadConn bool
|
|
||||||
for i := 0; i < maxBadConnRetries; i++ {
|
err = db.retry(func(strategy connReuseStrategy) error {
|
||||||
dc, err = db.conn(ctx, cachedOrNewConn)
|
dc, err = db.conn(ctx, strategy)
|
||||||
isBadConn = errors.Is(err, driver.ErrBadConn)
|
return err
|
||||||
if !isBadConn {
|
})
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isBadConn {
|
|
||||||
dc, err = db.conn(ctx, alwaysNewConn)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -2620,26 +2602,18 @@ func (s *Stmt) ExecContext(ctx context.Context, args ...any) (Result, error) {
|
|||||||
defer s.closemu.RUnlock()
|
defer s.closemu.RUnlock()
|
||||||
|
|
||||||
var res Result
|
var res Result
|
||||||
strategy := cachedOrNewConn
|
err := s.db.retry(func(strategy connReuseStrategy) error {
|
||||||
for i := 0; i < maxBadConnRetries+1; i++ {
|
|
||||||
if i == maxBadConnRetries {
|
|
||||||
strategy = alwaysNewConn
|
|
||||||
}
|
|
||||||
dc, releaseConn, ds, err := s.connStmt(ctx, strategy)
|
dc, releaseConn, ds, err := s.connStmt(ctx, strategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, driver.ErrBadConn) {
|
return err
|
||||||
continue
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = resultFromStatement(ctx, dc.ci, ds, args...)
|
res, err = resultFromStatement(ctx, dc.ci, ds, args...)
|
||||||
releaseConn(err)
|
releaseConn(err)
|
||||||
if !errors.Is(err, driver.ErrBadConn) {
|
return err
|
||||||
return res, err
|
})
|
||||||
}
|
|
||||||
}
|
return res, err
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exec executes a prepared statement with the given arguments and
|
// Exec executes a prepared statement with the given arguments and
|
||||||
@ -2768,24 +2742,19 @@ func (s *Stmt) QueryContext(ctx context.Context, args ...any) (*Rows, error) {
|
|||||||
defer s.closemu.RUnlock()
|
defer s.closemu.RUnlock()
|
||||||
|
|
||||||
var rowsi driver.Rows
|
var rowsi driver.Rows
|
||||||
strategy := cachedOrNewConn
|
var rows *Rows
|
||||||
for i := 0; i < maxBadConnRetries+1; i++ {
|
|
||||||
if i == maxBadConnRetries {
|
err := s.db.retry(func(strategy connReuseStrategy) error {
|
||||||
strategy = alwaysNewConn
|
|
||||||
}
|
|
||||||
dc, releaseConn, ds, err := s.connStmt(ctx, strategy)
|
dc, releaseConn, ds, err := s.connStmt(ctx, strategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, driver.ErrBadConn) {
|
return err
|
||||||
continue
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rowsi, err = rowsiFromStatement(ctx, dc.ci, ds, args...)
|
rowsi, err = rowsiFromStatement(ctx, dc.ci, ds, args...)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Note: ownership of ci passes to the *Rows, to be freed
|
// Note: ownership of ci passes to the *Rows, to be freed
|
||||||
// with releaseConn.
|
// with releaseConn.
|
||||||
rows := &Rows{
|
rows = &Rows{
|
||||||
dc: dc,
|
dc: dc,
|
||||||
rowsi: rowsi,
|
rowsi: rowsi,
|
||||||
// releaseConn set below
|
// releaseConn set below
|
||||||
@ -2805,15 +2774,14 @@ func (s *Stmt) QueryContext(ctx context.Context, args ...any) (*Rows, error) {
|
|||||||
txctx = s.cg.txCtx()
|
txctx = s.cg.txCtx()
|
||||||
}
|
}
|
||||||
rows.initContextClose(ctx, txctx)
|
rows.initContextClose(ctx, txctx)
|
||||||
return rows, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
releaseConn(err)
|
releaseConn(err)
|
||||||
if !errors.Is(err, driver.ErrBadConn) {
|
return err
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
}
|
return rows, err
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query executes a prepared query statement with the given arguments
|
// Query executes a prepared query statement with the given arguments
|
||||||
|
Loading…
Reference in New Issue
Block a user