Commit 4b90b7a2 authored by INADA Naoki's avatar INADA Naoki Committed by Brad Fitzpatrick

database/sql: add Pinger interface to driver Conn

Change-Id: If6eb3a7c9ad48a517e584567b1003479c1df6cca
Reviewed-on: https://go-review.googlesource.com/32136Reviewed-by: default avatarDaniel Theophanes <kardianos@gmail.com>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 398e861d
...@@ -69,6 +69,17 @@ var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented") ...@@ -69,6 +69,17 @@ var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented")
// you shouldn't return ErrBadConn. // you shouldn't return ErrBadConn.
var ErrBadConn = errors.New("driver: bad connection") var ErrBadConn = errors.New("driver: bad connection")
// Pinger is an optional interface that may be implemented by a Conn.
//
// If a Conn does not implement Pinger, the sql package's DB.Ping and
// DB.PingContext will check if there is at least one Conn available.
//
// If Conn.Ping returns ErrBadConn, DB.Ping and DB.PingContext will remove
// the Conn from pool.
type Pinger interface {
Ping(ctx context.Context) error
}
// Execer is an optional interface that may be implemented by a Conn. // Execer is an optional interface that may be implemented by a Conn.
// //
// If a Conn does not implement Execer, the sql package's DB.Exec will // If a Conn does not implement Execer, the sql package's DB.Exec will
......
...@@ -553,15 +553,27 @@ func Open(driverName, dataSourceName string) (*DB, error) { ...@@ -553,15 +553,27 @@ func Open(driverName, dataSourceName string) (*DB, error) {
// PingContext verifies a connection to the database is still alive, // PingContext verifies a connection to the database is still alive,
// establishing a connection if necessary. // establishing a connection if necessary.
func (db *DB) PingContext(ctx context.Context) error { func (db *DB) PingContext(ctx context.Context) error {
// TODO(bradfitz): give drivers an optional hook to implement var dc *driverConn
// this in a more efficient or more reliable way, if they var err error
// have one.
dc, err := db.conn(ctx, cachedOrNewConn) for i := 0; i < maxBadConnRetries; i++ {
dc, err = db.conn(ctx, cachedOrNewConn)
if err != driver.ErrBadConn {
break
}
}
if err == driver.ErrBadConn {
dc, err = db.conn(ctx, alwaysNewConn)
}
if err != nil { if err != nil {
return err return err
} }
db.putConn(dc, nil)
return nil if pinger, ok := dc.ci.(driver.Pinger); ok {
err = pinger.Ping(ctx)
}
db.putConn(dc, err)
return err
} }
// Ping verifies a connection to the database is still alive, // Ping verifies a connection to the database is still alive,
......
...@@ -2581,6 +2581,50 @@ func TestBadDriver(t *testing.T) { ...@@ -2581,6 +2581,50 @@ func TestBadDriver(t *testing.T) {
db.Exec("ignored") db.Exec("ignored")
} }
type pingDriver struct {
fails bool
}
type pingConn struct {
badConn
driver *pingDriver
}
var pingError = errors.New("Ping failed")
func (pc pingConn) Ping(ctx context.Context) error {
if pc.driver.fails {
return pingError
}
return nil
}
var _ driver.Pinger = pingConn{}
func (pd *pingDriver) Open(name string) (driver.Conn, error) {
return pingConn{driver: pd}, nil
}
func TestPing(t *testing.T) {
driver := &pingDriver{}
Register("ping", driver)
db, err := Open("ping", "ignored")
if err != nil {
t.Fatal(err)
}
if err := db.Ping(); err != nil {
t.Errorf("err was %#v, expected nil", err)
return
}
driver.fails = true
if err := db.Ping(); err != pingError {
t.Errorf("err was %#v, expected pingError", err)
}
}
func BenchmarkConcurrentDBExec(b *testing.B) { func BenchmarkConcurrentDBExec(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
ct := new(concurrentDBExecTest) ct := new(concurrentDBExecTest)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment