aboutsummaryrefslogtreecommitdiff
path: root/src/database/sql/sql_test.go
diff options
context:
space:
mode:
authorDaniel Theophanes <kardianos@gmail.com>2017-02-08 10:32:22 -0800
committerRuss Cox <rsc@golang.org>2017-02-09 01:12:19 +0000
commit4f6d4bb3f4461e7e25eff24254115b689495e834 (patch)
tree64eac492bd28bfafa42ce9a307010051e69cef8a /src/database/sql/sql_test.go
parentc57d91e34cf9a9d6d39b75e2f401bdf6a27447aa (diff)
downloadgo-4f6d4bb3f4461e7e25eff24254115b689495e834.tar.xz
database/sql: do not exhaust connection pool on conn request timeout
Previously if a context was canceled while it was waiting for a connection request, that connection request would leak. To prevent this remove the pending connection request if the context is canceled and ensure no connection has been sent on the channel. This requires a change to how the connection requests are represented in the DB. Fixes #18995 Change-Id: I9a274b48b8f4f7ca46cdee166faa38f56d030852 Reviewed-on: https://go-review.googlesource.com/36563 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/database/sql/sql_test.go')
-rw-r--r--src/database/sql/sql_test.go57
1 files changed, 57 insertions, 0 deletions
diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go
index 1cc38a5838..1c25e79d05 100644
--- a/src/database/sql/sql_test.go
+++ b/src/database/sql/sql_test.go
@@ -531,6 +531,63 @@ func TestQueryNamedArg(t *testing.T) {
}
}
+func TestPoolExhaustOnCancel(t *testing.T) {
+ if testing.Short() {
+ t.Skip("long test")
+ }
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+
+ max := 3
+
+ db.SetMaxOpenConns(max)
+
+ // First saturate the connection pool.
+ // Then start new requests for a connection that is cancelled after it is requested.
+
+ var saturate, saturateDone sync.WaitGroup
+ saturate.Add(max)
+ saturateDone.Add(max)
+
+ for i := 0; i < max; i++ {
+ go func() {
+ saturate.Done()
+ rows, err := db.Query("WAIT|500ms|SELECT|people|name,photo|")
+ if err != nil {
+ t.Fatalf("Query: %v", err)
+ }
+ rows.Close()
+ saturateDone.Done()
+ }()
+ }
+
+ saturate.Wait()
+
+ // Now cancel the request while it is waiting.
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
+ defer cancel()
+
+ for i := 0; i < max; i++ {
+ ctxReq, cancelReq := context.WithCancel(ctx)
+ go func() {
+ time.Sleep(time.Millisecond * 100)
+ cancelReq()
+ }()
+ err := db.PingContext(ctxReq)
+ if err != context.Canceled {
+ t.Fatalf("PingContext (Exhaust): %v", err)
+ }
+ }
+
+ saturateDone.Wait()
+
+ // Now try to open a normal connection.
+ err := db.PingContext(ctx)
+ if err != nil {
+ t.Fatalf("PingContext (Normal): %v", err)
+ }
+}
+
func TestByteOwnership(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)