From fa56a8fcad4ef6c2d582c4e49654902b3ba899e9 Mon Sep 17 00:00:00 2001 From: Matthew Herrmann Date: Thu, 31 Oct 2019 20:01:14 -0700 Subject: [PATCH 1/3] -a Fix connection leak caused by rapid context cancellation (https://github.com/go-sql-driver/mysql/issues/1023) --- connector.go | 1 + 1 file changed, 1 insertion(+) diff --git a/connector.go b/connector.go index 5aaaba43e..bbad4e23b 100644 --- a/connector.go +++ b/connector.go @@ -64,6 +64,7 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { // Call startWatcher for context support (From Go 1.8) mc.startWatcher() if err := mc.watchCancel(ctx); err != nil { + mc.cleanup() return nil, err } defer mc.finish() From 65d32e4375a2970804833978b31f8e0198917349 Mon Sep 17 00:00:00 2001 From: Matthew Herrmann Date: Thu, 31 Oct 2019 20:37:42 -0700 Subject: [PATCH 2/3] -a Regression test for https://github.com/go-sql-driver/mysql/issues/1023. --- driver_go110_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/driver_go110_test.go b/driver_go110_test.go index 19a0e5956..d83e31ab9 100644 --- a/driver_go110_test.go +++ b/driver_go110_test.go @@ -135,3 +135,55 @@ func TestConnectorTimeoutsDuringOpen(t *testing.T) { t.Fatalf("(*Connector).Connect should have timed out") } } + +// A connection which can only be closed. +type dummyConnection struct { + net.Conn + closed bool +} + +func (d *dummyConnection) Close() error { + d.closed = true + return nil +} + +func TestConnectorTimeoutsWatchCancel(t *testing.T) { + var ( + cancel func() // Used to cancel the context just after connecting. + created *dummyConnection // The created connection. + ) + + RegisterDialContext("TestConnectorTimeoutsWatchCancel", func(ctx context.Context, addr string) (net.Conn, error) { + // Canceling at this time triggers the watchCancel error branch in Connect(). + cancel() + created = &dummyConnection{} + return created, nil + }) + + mycnf := NewConfig() + mycnf.User = "root" + mycnf.Addr = "foo" + mycnf.Net = "TestConnectorTimeoutsWatchCancel" + + conn, err := NewConnector(mycnf) + if err != nil { + t.Fatal(err) + } + + db := sql.OpenDB(conn) + defer db.Close() + + var ctx context.Context + ctx, cancel = context.WithCancel(context.Background()) + + if _, err := db.Conn(ctx); err != context.Canceled { + t.Errorf("got %v, want context.Canceled", err) + } + + if created == nil { + t.Fatal("no connection created") + } + if !created.closed { + t.Errorf("connection not closed") + } +} From 12b2ae1513db26f1f7414b6c50b53568d42f63ab Mon Sep 17 00:00:00 2001 From: Matthew Herrmann Date: Thu, 31 Oct 2019 20:54:17 -0700 Subject: [PATCH 3/3] -a fix go vet warning for possibly-unused cancel (https://github.com/go-sql-driver/mysql/issues/1023) --- driver_go110_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/driver_go110_test.go b/driver_go110_test.go index d83e31ab9..fd8df8975 100644 --- a/driver_go110_test.go +++ b/driver_go110_test.go @@ -175,6 +175,7 @@ func TestConnectorTimeoutsWatchCancel(t *testing.T) { var ctx context.Context ctx, cancel = context.WithCancel(context.Background()) + defer cancel() if _, err := db.Conn(ctx); err != context.Canceled { t.Errorf("got %v, want context.Canceled", err)