Skip to content

Commit 4b90b7a

Browse files
methanebradfitz
authored andcommitted
database/sql: add Pinger interface to driver Conn
Change-Id: If6eb3a7c9ad48a517e584567b1003479c1df6cca Reviewed-on: https://go-review.googlesource.com/32136 Reviewed-by: Daniel Theophanes <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 398e861 commit 4b90b7a

File tree

3 files changed

+73
-6
lines changed

3 files changed

+73
-6
lines changed

src/database/sql/driver/driver.go

+11
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented")
6969
// you shouldn't return ErrBadConn.
7070
var ErrBadConn = errors.New("driver: bad connection")
7171

72+
// Pinger is an optional interface that may be implemented by a Conn.
73+
//
74+
// If a Conn does not implement Pinger, the sql package's DB.Ping and
75+
// DB.PingContext will check if there is at least one Conn available.
76+
//
77+
// If Conn.Ping returns ErrBadConn, DB.Ping and DB.PingContext will remove
78+
// the Conn from pool.
79+
type Pinger interface {
80+
Ping(ctx context.Context) error
81+
}
82+
7283
// Execer is an optional interface that may be implemented by a Conn.
7384
//
7485
// If a Conn does not implement Execer, the sql package's DB.Exec will

src/database/sql/sql.go

+18-6
Original file line numberDiff line numberDiff line change
@@ -553,15 +553,27 @@ func Open(driverName, dataSourceName string) (*DB, error) {
553553
// PingContext verifies a connection to the database is still alive,
554554
// establishing a connection if necessary.
555555
func (db *DB) PingContext(ctx context.Context) error {
556-
// TODO(bradfitz): give drivers an optional hook to implement
557-
// this in a more efficient or more reliable way, if they
558-
// have one.
559-
dc, err := db.conn(ctx, cachedOrNewConn)
556+
var dc *driverConn
557+
var err error
558+
559+
for i := 0; i < maxBadConnRetries; i++ {
560+
dc, err = db.conn(ctx, cachedOrNewConn)
561+
if err != driver.ErrBadConn {
562+
break
563+
}
564+
}
565+
if err == driver.ErrBadConn {
566+
dc, err = db.conn(ctx, alwaysNewConn)
567+
}
560568
if err != nil {
561569
return err
562570
}
563-
db.putConn(dc, nil)
564-
return nil
571+
572+
if pinger, ok := dc.ci.(driver.Pinger); ok {
573+
err = pinger.Ping(ctx)
574+
}
575+
db.putConn(dc, err)
576+
return err
565577
}
566578

567579
// Ping verifies a connection to the database is still alive,

src/database/sql/sql_test.go

+44
Original file line numberDiff line numberDiff line change
@@ -2581,6 +2581,50 @@ func TestBadDriver(t *testing.T) {
25812581
db.Exec("ignored")
25822582
}
25832583

2584+
type pingDriver struct {
2585+
fails bool
2586+
}
2587+
2588+
type pingConn struct {
2589+
badConn
2590+
driver *pingDriver
2591+
}
2592+
2593+
var pingError = errors.New("Ping failed")
2594+
2595+
func (pc pingConn) Ping(ctx context.Context) error {
2596+
if pc.driver.fails {
2597+
return pingError
2598+
}
2599+
return nil
2600+
}
2601+
2602+
var _ driver.Pinger = pingConn{}
2603+
2604+
func (pd *pingDriver) Open(name string) (driver.Conn, error) {
2605+
return pingConn{driver: pd}, nil
2606+
}
2607+
2608+
func TestPing(t *testing.T) {
2609+
driver := &pingDriver{}
2610+
Register("ping", driver)
2611+
2612+
db, err := Open("ping", "ignored")
2613+
if err != nil {
2614+
t.Fatal(err)
2615+
}
2616+
2617+
if err := db.Ping(); err != nil {
2618+
t.Errorf("err was %#v, expected nil", err)
2619+
return
2620+
}
2621+
2622+
driver.fails = true
2623+
if err := db.Ping(); err != pingError {
2624+
t.Errorf("err was %#v, expected pingError", err)
2625+
}
2626+
}
2627+
25842628
func BenchmarkConcurrentDBExec(b *testing.B) {
25852629
b.ReportAllocs()
25862630
ct := new(concurrentDBExecTest)

0 commit comments

Comments
 (0)