Skip to content

Commit e339231

Browse files
committed
GODRIVER-3340 Add a test for goroutine leaks.
1 parent e4c2740 commit e339231

File tree

5 files changed

+249
-0
lines changed

5 files changed

+249
-0
lines changed

.evergreen/config.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,17 @@ functions:
615615
bash etc/run_docker.sh
616616
TOPOLOGY=sharded_cluster bash etc/run_docker.sh test-short
617617
618+
run-goleak-test:
619+
- command: shell.exec
620+
type: test
621+
params:
622+
shell: "bash"
623+
working_dir: src/go.mongodb.org/mongo-driver
624+
include_expansions_in_env: ["MONGODB_URI"]
625+
script: |
626+
${PREPARE_SHELL}
627+
bash etc/run-goleak-test.sh
628+
618629
run-valid-ocsp-server:
619630
- command: shell.exec
620631
params:
@@ -1420,6 +1431,15 @@ tasks:
14201431
- func: bootstrap-mongo-orchestration
14211432
- func: run-docker-test
14221433

1434+
- name: test-goroutine-leaks
1435+
commands:
1436+
- func: bootstrap-mongo-orchestration
1437+
vars:
1438+
TOPOLOGY: "replica_set"
1439+
AUTH: "noauth"
1440+
SSL: "nossl"
1441+
- func: run-goleak-test
1442+
14231443
- name: test-load-balancer-noauth-nossl
14241444
tags: ["load-balancer"]
14251445
commands:
@@ -2539,6 +2559,16 @@ buildvariants:
25392559
tasks:
25402560
- name: "test-docker-runner"
25412561

2562+
- name: goroutine-leaks-test
2563+
tags: ["pullrequest"]
2564+
display_name: "Goroutine Leaks Test"
2565+
run_on:
2566+
- ubuntu2204-large
2567+
expansions:
2568+
GO_DIST: "/opt/golang/go1.22"
2569+
tasks:
2570+
- name: "test-goroutine-leaks"
2571+
25422572
- matrix_name: "tests-rhel-36-with-zlib-support"
25432573
tags: ["pullrequest"]
25442574
matrix_spec: { version: ["3.6"], os-ssl-32: ["rhel87-64"] }

etc/run-goleak-test.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env bash
2+
# run-goleak-test
3+
# Run goroutine leak tests.
4+
set -eu
5+
set +x
6+
7+
echo "Running internal/test/goleak"
8+
pushd internal/test/goleak
9+
go test -v ./... >> ../../../test.suite
10+
popd

internal/test/goleak/go.mod

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module go.mongodb.go/mongo-driver/internal/test/goleak
2+
3+
go 1.22
4+
5+
replace go.mongodb.org/mongo-driver => ../../../
6+
7+
// Note that the Go driver version is replaced with the local Go driver code
8+
// by the replace directive above.
9+
require go.mongodb.org/mongo-driver v1.13.1
10+
11+
require (
12+
github.com/stretchr/testify v1.9.0
13+
go.uber.org/goleak v1.3.0
14+
)
15+
16+
require (
17+
github.com/davecgh/go-spew v1.1.1 // indirect
18+
github.com/golang/snappy v0.0.4 // indirect
19+
github.com/klauspost/compress v1.16.7 // indirect
20+
github.com/kr/pretty v0.3.0 // indirect
21+
github.com/montanaflynn/stats v0.7.1 // indirect
22+
github.com/pmezard/go-difflib v1.0.0 // indirect
23+
github.com/rogpeppe/go-internal v1.8.1 // indirect
24+
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
25+
github.com/xdg-go/scram v1.1.2 // indirect
26+
github.com/xdg-go/stringprep v1.0.4 // indirect
27+
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
28+
golang.org/x/crypto v0.26.0 // indirect
29+
golang.org/x/sync v0.8.0 // indirect
30+
golang.org/x/text v0.17.0 // indirect
31+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
32+
gopkg.in/yaml.v3 v3.0.1 // indirect
33+
)

internal/test/goleak/go.sum

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
5+
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
6+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
7+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
8+
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
9+
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
10+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
11+
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
12+
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
13+
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
14+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
15+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
16+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
17+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
18+
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
19+
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
20+
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
21+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
22+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
23+
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
24+
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
25+
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
26+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
27+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
28+
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
29+
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
30+
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
31+
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
32+
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
33+
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
34+
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
35+
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
36+
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
37+
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
38+
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
39+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
40+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
41+
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
42+
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
43+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
44+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
45+
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
46+
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
47+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
48+
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
49+
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
50+
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
51+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
52+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
53+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
54+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
55+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
56+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
57+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
58+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
59+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
60+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
61+
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
62+
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
63+
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
64+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
65+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
66+
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
67+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
68+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
69+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
70+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
71+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
72+
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
73+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
74+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/test/goleak/goleak_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright (C) MongoDB, Inc. 2024-present.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
// not use this file except in compliance with the License. You may obtain
5+
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
package main
8+
9+
import (
10+
"context"
11+
"fmt"
12+
"os"
13+
"testing"
14+
"time"
15+
16+
"github.com/stretchr/testify/require"
17+
"go.mongodb.org/mongo-driver/bson"
18+
"go.mongodb.org/mongo-driver/mongo"
19+
"go.mongodb.org/mongo-driver/mongo/options"
20+
"go.uber.org/goleak"
21+
)
22+
23+
var dbName = fmt.Sprintf("goleak-%d", time.Now().Unix())
24+
25+
func TestMain(m *testing.M) {
26+
goleak.VerifyTestMain(m)
27+
}
28+
29+
// TestGoroutineLeak creates clients with various client configurations, runs
30+
// some operations with each one, then disconnects the client. It asserts that
31+
// no goroutines were leaked after the client is disconnected.
32+
func TestGoroutineLeak(t *testing.T) {
33+
uri := "mongodb://localhost:27017"
34+
if u := os.Getenv("MONGODB_URI"); u != "" {
35+
uri = u
36+
}
37+
38+
testCases := []struct {
39+
desc string
40+
opts *options.ClientOptions
41+
}{
42+
{
43+
desc: "base",
44+
opts: options.Client().ApplyURI(uri),
45+
},
46+
{
47+
desc: "compressors=snappy",
48+
opts: options.Client().ApplyURI(uri).SetCompressors([]string{"snappy"}),
49+
},
50+
{
51+
desc: "compressors=zlib",
52+
opts: options.Client().ApplyURI(uri).SetCompressors([]string{"zlib"}),
53+
},
54+
{
55+
desc: "compressors=zstd",
56+
opts: options.Client().ApplyURI(uri).SetCompressors([]string{"zstd"}),
57+
},
58+
{
59+
desc: "minPoolSize=10",
60+
opts: options.Client().ApplyURI(uri).SetMinPoolSize(10),
61+
},
62+
{
63+
desc: "serverMonitoringMode=poll",
64+
opts: options.Client().ApplyURI(uri).SetServerMonitoringMode(options.ServerMonitoringModePoll),
65+
},
66+
}
67+
for _, tc := range testCases {
68+
// These can't be run in parallel because goleak currently can't filter
69+
// out goroutines from other parallel subtests.
70+
t.Run(tc.desc, func(t *testing.T) {
71+
defer goleak.VerifyNone(t)
72+
73+
client, err := mongo.Connect(context.Background(), tc.opts)
74+
require.NoError(t, err)
75+
76+
defer func() {
77+
err = client.Disconnect(context.Background())
78+
require.NoError(t, err)
79+
}()
80+
81+
coll := client.Database(dbName).Collection(collectionName(t))
82+
defer func() {
83+
err := coll.Drop(context.Background())
84+
require.NoError(t, err)
85+
}()
86+
87+
_, err = coll.InsertOne(context.Background(), bson.M{"x": 123})
88+
require.NoError(t, err)
89+
90+
for i := 0; i < 20; i++ {
91+
var res bson.D
92+
err = coll.FindOne(context.Background(), bson.D{}).Decode(&res)
93+
require.NoError(t, err)
94+
time.Sleep(50 * time.Millisecond)
95+
}
96+
})
97+
}
98+
}
99+
100+
func collectionName(t *testing.T) string {
101+
return fmt.Sprintf("%s-%d", t.Name(), time.Now().Unix())
102+
}

0 commit comments

Comments
 (0)