diff options
| author | Julien Schmidt <google@julienschmidt.com> | 2013-12-17 11:57:30 -0800 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2013-12-17 11:57:30 -0800 |
| commit | 762a9d934eab267418595df7a220eec50919b77d (patch) | |
| tree | 327c8c64f2d4b573bc70f54db7846df72508d6b0 /src/pkg/database/sql/sql.go | |
| parent | a075fdbaa647879c37b1969437366b9c94acd010 (diff) | |
| download | go-762a9d934eab267418595df7a220eec50919b77d.tar.xz | |
database/sql: fix auto-reconnect in prepared statements
This also fixes several connection leaks.
Fixes #5718
R=bradfitz, adg
CC=alberto.garcia.hierro, golang-dev
https://golang.org/cl/14920046
Diffstat (limited to 'src/pkg/database/sql/sql.go')
| -rw-r--r-- | src/pkg/database/sql/sql.go | 120 |
1 files changed, 69 insertions, 51 deletions
diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go index fae109f252..df989cd66c 100644 --- a/src/pkg/database/sql/sql.go +++ b/src/pkg/database/sql/sql.go @@ -256,7 +256,7 @@ func (dc *driverConn) prepareLocked(query string) (driver.Stmt, error) { // stmt closes if the conn is about to close anyway? For now // do the safe thing, in case stmts need to be closed. // - // TODO(bradfitz): after Go 1.1, closing driver.Stmts + // TODO(bradfitz): after Go 1.2, closing driver.Stmts // should be moved to driverStmt, using unique // *driverStmts everywhere (including from // *Stmt.connStmt, instead of returning a @@ -798,13 +798,17 @@ func (db *DB) putConnDBLocked(dc *driverConn, err error) bool { return false } +// maxBadConnRetries is the number of maximum retries if the driver returns +// driver.ErrBadConn to signal a broken connection. +const maxBadConnRetries = 10 + // Prepare creates a prepared statement for later queries or executions. // Multiple queries or executions may be run concurrently from the // returned statement. func (db *DB) Prepare(query string) (*Stmt, error) { var stmt *Stmt var err error - for i := 0; i < 10; i++ { + for i := 0; i < maxBadConnRetries; i++ { stmt, err = db.prepare(query) if err != driver.ErrBadConn { break @@ -846,7 +850,7 @@ func (db *DB) prepare(query string) (*Stmt, error) { func (db *DB) Exec(query string, args ...interface{}) (Result, error) { var res Result var err error - for i := 0; i < 10; i++ { + for i := 0; i < maxBadConnRetries; i++ { res, err = db.exec(query, args) if err != driver.ErrBadConn { break @@ -895,7 +899,7 @@ func (db *DB) exec(query string, args []interface{}) (res Result, err error) { func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { var rows *Rows var err error - for i := 0; i < 10; i++ { + for i := 0; i < maxBadConnRetries; i++ { rows, err = db.query(query, args) if err != driver.ErrBadConn { break @@ -983,7 +987,7 @@ func (db *DB) QueryRow(query string, args ...interface{}) *Row { func (db *DB) Begin() (*Tx, error) { var tx *Tx var err error - for i := 0; i < 10; i++ { + for i := 0; i < maxBadConnRetries; i++ { tx, err = db.begin() if err != driver.ErrBadConn { break @@ -1245,13 +1249,24 @@ type Stmt struct { func (s *Stmt) Exec(args ...interface{}) (Result, error) { s.closemu.RLock() defer s.closemu.RUnlock() - dc, releaseConn, si, err := s.connStmt() - if err != nil { - return nil, err - } - defer releaseConn(nil) - return resultFromStatement(driverStmt{dc, si}, args...) + var res Result + for i := 0; i < maxBadConnRetries; i++ { + dc, releaseConn, si, err := s.connStmt() + if err != nil { + if err == driver.ErrBadConn { + continue + } + return nil, err + } + + res, err = resultFromStatement(driverStmt{dc, si}, args...) + releaseConn(err) + if err != driver.ErrBadConn { + return res, err + } + } + return nil, driver.ErrBadConn } func resultFromStatement(ds driverStmt, args ...interface{}) (Result, error) { @@ -1329,26 +1344,21 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St // Make a new conn if all are busy. // TODO(bradfitz): or wait for one? make configurable later? if !match { - for i := 0; ; i++ { - dc, err := s.db.conn() - if err != nil { - return nil, nil, nil, err - } - dc.Lock() - si, err := dc.prepareLocked(s.query) - dc.Unlock() - if err == driver.ErrBadConn && i < 10 { - continue - } - if err != nil { - return nil, nil, nil, err - } - s.mu.Lock() - cs = connStmt{dc, si} - s.css = append(s.css, cs) - s.mu.Unlock() - break + dc, err := s.db.conn() + if err != nil { + return nil, nil, nil, err } + dc.Lock() + si, err := dc.prepareLocked(s.query) + dc.Unlock() + if err != nil { + s.db.putConn(dc, err) + return nil, nil, nil, err + } + s.mu.Lock() + cs = connStmt{dc, si} + s.css = append(s.css, cs) + s.mu.Unlock() } conn := cs.dc @@ -1361,31 +1371,39 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) { s.closemu.RLock() defer s.closemu.RUnlock() - dc, releaseConn, si, err := s.connStmt() - if err != nil { - return nil, err - } + var rowsi driver.Rows + for i := 0; i < maxBadConnRetries; i++ { + dc, releaseConn, si, err := s.connStmt() + if err != nil { + if err == driver.ErrBadConn { + continue + } + return nil, err + } - ds := driverStmt{dc, si} - rowsi, err := rowsiFromStatement(ds, args...) - if err != nil { - releaseConn(err) - return nil, err - } + rowsi, err = rowsiFromStatement(driverStmt{dc, si}, args...) + if err == nil { + // Note: ownership of ci passes to the *Rows, to be freed + // with releaseConn. + rows := &Rows{ + dc: dc, + rowsi: rowsi, + // releaseConn set below + } + s.db.addDep(s, rows) + rows.releaseConn = func(err error) { + releaseConn(err) + s.db.removeDep(s, rows) + } + return rows, nil + } - // Note: ownership of ci passes to the *Rows, to be freed - // with releaseConn. - rows := &Rows{ - dc: dc, - rowsi: rowsi, - // releaseConn set below - } - s.db.addDep(s, rows) - rows.releaseConn = func(err error) { releaseConn(err) - s.db.removeDep(s, rows) + if err != driver.ErrBadConn { + return nil, err + } } - return rows, nil + return nil, driver.ErrBadConn } func rowsiFromStatement(ds driverStmt, args ...interface{}) (driver.Rows, error) { |
