Description
Issue description
Func (mc *mysqlConn)watchCancel() set var watching to true when conn request timeout and return err context deadline exceeded, exec mc.cleanup() to close the connection.but in standard lib, only when err equal to ErrBadConn ,the conn would be closed. Otherwise, err would be ignored and connection will be put back into conn pool.Next time I get connection from conn pool, I would get a bad connection which had been close by driver because of err deadline exceeded.
Example code
In go-sql-driver
func (mc *mysqlConn) watchCancel(ctx context.Context) error {
if mc.watching {
// Reach here if canceled,
// so the connection is already invalid
mc.cleanup()
return nil
}
if ctx.Done() == nil {
return nil
}
// watching would be set to true if ctx.Done() return err, and mc.cleanup() would to exec next time
// connection would be closed .
mc.watching = true
select {
default:
case <-ctx.Done():
return ctx.Err()
}
if mc.watcher == nil {
return nil
}
mc.watcher <- ctx
return nil
}
But in standard Lib. function putConn would put conn back to pool unless err equal to ErrBadConn:
https://github.com/golang/go/blob/release-branch.go1.8/src/database/sql/sql.go#L1032.
Otherwise, err would be ignored at https://github.com/golang/go/blob/release-branch.go1.8/src/database/sql/sql.go#L1045 and conn would be put back into pool.
func (db *DB) putConn(dc *driverConn, err error) {
db.mu.Lock()
if !dc.inUse {
if debugGetPut {
fmt.Printf("putConn(%v) DUPLICATE was: %s\n\nPREVIOUS was: %s", dc, stack(), db.lastPut[dc])
}
panic("sql: connection returned that was never out")
}
if debugGetPut {
db.lastPut[dc] = stack()
}
dc.inUse = false
for _, fn := range dc.onPut {
fn()
}
dc.onPut = nil
// Here, only if err equal to ErrBadConn conn would be closed
if err == driver.ErrBadConn {
// Don't reuse bad connections.
// Since the conn is considered bad and is being discarded, treat it
// as closed. Don't decrement the open count here, finalClose will
// take care of that.
db.maybeOpenNewConnections()
db.mu.Unlock()
dc.Close()
return
}
if putConnHook != nil {
putConnHook(db, dc)
}
// Here, conn would be put back into pool.next time i get conn from pool, i would get a bad conn.
added := db.putConnDBLocked(dc, nil)
db.mu.Unlock()
if !added {
dc.Close()
}
}
Error log
[mysql] 2017/08/29 19:52:06 connection_go18.go:23: invalid connection
Configuration
*Driver version (or git SHA):i3UtE7/Cn57eX1hO5Z0CqY/Eeb4=
*Go version: go1.8.3 darwin/amd64
*Server version: MySQL 5.6, MariaDB 10.0.20
*Server OS: darwin/amd64