Commit 90e2e2b8 authored by Marko Tiikkaja's avatar Marko Tiikkaja Committed by Brad Fitzpatrick

database/sql: Avoid re-preparing statements when all connections are busy

Previously, if all connections were busy, we would always
re-prepare the statement on the connection we were assigned from
the pool.  That meant that if all connections were busy most of the
time, the number of prepared statements for each connection would
keep increasing over time.

Instead, after getting a free connection, check to see if the
statement has already been prepared on it, and reuse the statement
handle if so.

LGTM=bradfitz
R=golang-codereviews, gobot, bradfitz
CC=golang-codereviews
https://golang.org/cl/116930043
parent 60447c2d
...@@ -1326,15 +1326,12 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St ...@@ -1326,15 +1326,12 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
return ci, releaseConn, s.txsi.si, nil return ci, releaseConn, s.txsi.si, nil
} }
var cs connStmt
match := false
for i := 0; i < len(s.css); i++ { for i := 0; i < len(s.css); i++ {
v := s.css[i] v := s.css[i]
_, err := s.db.connIfFree(v.dc) _, err := s.db.connIfFree(v.dc)
if err == nil { if err == nil {
match = true s.mu.Unlock()
cs = v return v.dc, v.dc.releaseConn, v.si, nil
break
} }
if err == errConnClosed { if err == errConnClosed {
// Lazily remove dead conn from our freelist. // Lazily remove dead conn from our freelist.
...@@ -1346,28 +1343,41 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St ...@@ -1346,28 +1343,41 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St
} }
s.mu.Unlock() s.mu.Unlock()
// Make a new conn if all are busy. // If all connections are busy, either wait for one to become available (if
// TODO(bradfitz): or wait for one? make configurable later? // we've already hit the maximum number of open connections) or create a
if !match { // new one.
//
// TODO(bradfitz): or always wait for one? make configurable later?
dc, err := s.db.conn() dc, err := s.db.conn()
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
// Do another pass over the list to see whether this statement has
// already been prepared on the connection assigned to us.
s.mu.Lock()
for _, v := range s.css {
if v.dc == dc {
s.mu.Unlock()
return dc, dc.releaseConn, v.si, nil
}
}
s.mu.Unlock()
// No luck; we need to prepare the statement on this connection
dc.Lock() dc.Lock()
si, err := dc.prepareLocked(s.query) si, err = dc.prepareLocked(s.query)
dc.Unlock() dc.Unlock()
if err != nil { if err != nil {
s.db.putConn(dc, err) s.db.putConn(dc, err)
return nil, nil, nil, err return nil, nil, nil, err
} }
s.mu.Lock() s.mu.Lock()
cs = connStmt{dc, si} cs := connStmt{dc, si}
s.css = append(s.css, cs) s.css = append(s.css, cs)
s.mu.Unlock() s.mu.Unlock()
}
conn := cs.dc return dc, dc.releaseConn, si, nil
return conn, conn.releaseConn, cs.si, nil
} }
// Query executes a prepared query statement with the given arguments // Query executes a prepared query statement with the given arguments
......
...@@ -1348,6 +1348,11 @@ func TestErrBadConnReconnect(t *testing.T) { ...@@ -1348,6 +1348,11 @@ func TestErrBadConnReconnect(t *testing.T) {
return nil return nil
}) })
// Provide a way to force a re-prepare of a statement on next execution
forcePrepare := func(stmt *Stmt) {
stmt.css = nil
}
// stmt.Exec // stmt.Exec
stmt1, err := db.Prepare("INSERT|t1|name=?,age=?,dead=?") stmt1, err := db.Prepare("INSERT|t1|name=?,age=?,dead=?")
if err != nil { if err != nil {
...@@ -1355,9 +1360,7 @@ func TestErrBadConnReconnect(t *testing.T) { ...@@ -1355,9 +1360,7 @@ func TestErrBadConnReconnect(t *testing.T) {
} }
defer stmt1.Close() defer stmt1.Close()
// make sure we must prepare the stmt first // make sure we must prepare the stmt first
for _, cs := range stmt1.css { forcePrepare(stmt1)
cs.dc.inUse = true
}
stmtExec := func() error { stmtExec := func() error {
_, err := stmt1.Exec("Gopher", 3, false) _, err := stmt1.Exec("Gopher", 3, false)
...@@ -1373,9 +1376,7 @@ func TestErrBadConnReconnect(t *testing.T) { ...@@ -1373,9 +1376,7 @@ func TestErrBadConnReconnect(t *testing.T) {
} }
defer stmt2.Close() defer stmt2.Close()
// make sure we must prepare the stmt first // make sure we must prepare the stmt first
for _, cs := range stmt2.css { forcePrepare(stmt2)
cs.dc.inUse = true
}
stmtQuery := func() error { stmtQuery := func() error {
rows, err := stmt2.Query() rows, err := stmt2.Query()
......
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