Skip to content

Commit 296987f

Browse files
mherr-googlemethane
authored andcommitted
Fix connection leak caused by rapid context cancellation (mysqljs#1024)
Fixes mysqljs#1023
1 parent 6ea7374 commit 296987f

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

connector.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
6464
// Call startWatcher for context support (From Go 1.8)
6565
mc.startWatcher()
6666
if err := mc.watchCancel(ctx); err != nil {
67+
mc.cleanup()
6768
return nil, err
6869
}
6970
defer mc.finish()

driver_go110_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,56 @@ func TestConnectorTimeoutsDuringOpen(t *testing.T) {
135135
t.Fatalf("(*Connector).Connect should have timed out")
136136
}
137137
}
138+
139+
// A connection which can only be closed.
140+
type dummyConnection struct {
141+
net.Conn
142+
closed bool
143+
}
144+
145+
func (d *dummyConnection) Close() error {
146+
d.closed = true
147+
return nil
148+
}
149+
150+
func TestConnectorTimeoutsWatchCancel(t *testing.T) {
151+
var (
152+
cancel func() // Used to cancel the context just after connecting.
153+
created *dummyConnection // The created connection.
154+
)
155+
156+
RegisterDialContext("TestConnectorTimeoutsWatchCancel", func(ctx context.Context, addr string) (net.Conn, error) {
157+
// Canceling at this time triggers the watchCancel error branch in Connect().
158+
cancel()
159+
created = &dummyConnection{}
160+
return created, nil
161+
})
162+
163+
mycnf := NewConfig()
164+
mycnf.User = "root"
165+
mycnf.Addr = "foo"
166+
mycnf.Net = "TestConnectorTimeoutsWatchCancel"
167+
168+
conn, err := NewConnector(mycnf)
169+
if err != nil {
170+
t.Fatal(err)
171+
}
172+
173+
db := sql.OpenDB(conn)
174+
defer db.Close()
175+
176+
var ctx context.Context
177+
ctx, cancel = context.WithCancel(context.Background())
178+
defer cancel()
179+
180+
if _, err := db.Conn(ctx); err != context.Canceled {
181+
t.Errorf("got %v, want context.Canceled", err)
182+
}
183+
184+
if created == nil {
185+
t.Fatal("no connection created")
186+
}
187+
if !created.closed {
188+
t.Errorf("connection not closed")
189+
}
190+
}

0 commit comments

Comments
 (0)