Skip to content
This repository was archived by the owner on Apr 19, 2024. It is now read-only.

Commit f740f2b

Browse files
authored
fix: goroutine leaks (#221)
* fix: goroutine leaks * go mod tidy * os.Exit() does not run deferred fns * prefer defer * more leaks identified * remove code that is commented * remove commented code * fix test
1 parent 1b5f31d commit f740f2b

File tree

8 files changed

+34
-22
lines changed

8 files changed

+34
-22
lines changed

cluster/cluster_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@ import (
2323
"github.com/mailgun/gubernator/v2/cluster"
2424
"github.com/stretchr/testify/assert"
2525
"github.com/stretchr/testify/require"
26+
"go.uber.org/goleak"
2627
)
2728

2829
func TestStartMultipleInstances(t *testing.T) {
30+
t.Cleanup(func() {
31+
goleak.VerifyNone(t)
32+
})
2933
err := cluster.Start(2)
3034
require.NoError(t, err)
31-
defer cluster.Stop()
35+
t.Cleanup(cluster.Stop)
3236

3337
assert.Equal(t, 2, len(cluster.GetPeers()))
3438
assert.Equal(t, 2, len(cluster.GetDaemons()))

cmd/gubernator/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import (
3131
"github.com/sirupsen/logrus"
3232
"go.opentelemetry.io/otel/sdk/resource"
3333
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
34-
"k8s.io/klog"
34+
"k8s.io/klog/v2"
3535
)
3636

3737
var log = logrus.WithField("category", "gubernator")

daemon.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"crypto/tls"
2222
"fmt"
23+
"io"
2324
"log"
2425
"net"
2526
"net/http"
@@ -52,6 +53,7 @@ type Daemon struct {
5253
PeerInfo PeerInfo
5354

5455
log FieldLogger
56+
logWriter *io.PipeWriter
5557
pool PoolInterface
5658
conf DaemonConfig
5759
httpSrv *http.Server
@@ -288,7 +290,8 @@ func (s *Daemon) Start(ctx context.Context) error {
288290
s.promRegister, promhttp.HandlerFor(s.promRegister, promhttp.HandlerOpts{}),
289291
))
290292
mux.Handle("/", gateway)
291-
log := log.New(newLogWriter(s.log), "", 0)
293+
s.logWriter = newLogWriter(s.log)
294+
log := log.New(s.logWriter, "", 0)
292295
s.httpSrv = &http.Server{Addr: s.conf.HTTPListenAddress, Handler: mux, ErrorLog: log}
293296

294297
s.HTTPListener, err = net.Listen("tcp", s.conf.HTTPListenAddress)
@@ -382,6 +385,8 @@ func (s *Daemon) Close() {
382385
s.log.Infof("GRPC close for %s ...", s.GRPCListeners[i].Addr())
383386
srv.GracefulStop()
384387
}
388+
s.logWriter.Close()
389+
_ = s.V1Server.Close()
385390
s.wg.Stop()
386391
s.statsHandler.Close()
387392
s.gwCancel()

functional_test.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,11 @@ func TestMain(m *testing.M) {
6161
fmt.Println(err)
6262
os.Exit(1)
6363
}
64-
defer cluster.Stop()
65-
os.Exit(m.Run())
64+
code := m.Run()
65+
cluster.Stop()
66+
67+
// os.Exit doesn't run deferred functions
68+
os.Exit(code)
6669
}
6770

6871
func TestOverTheLimit(t *testing.T) {

global.go

+2
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ func (gm *globalManager) runAsyncHits() {
127127
hits = make(map[string]*RateLimitReq)
128128
}
129129
case <-done:
130+
interval.Stop()
130131
return false
131132
}
132133
return true
@@ -216,6 +217,7 @@ func (gm *globalManager) runBroadcasts() {
216217
gm.metricGlobalQueueLength.Set(0)
217218
}
218219
case <-done:
220+
interval.Stop()
219221
return false
220222
}
221223
return true

go.mod

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ require (
2222
go.opentelemetry.io/otel v1.21.0
2323
go.opentelemetry.io/otel/sdk v1.21.0
2424
go.opentelemetry.io/otel/trace v1.21.0
25+
go.uber.org/goleak v1.3.0
2526
golang.org/x/net v0.18.0
2627
golang.org/x/time v0.3.0
2728
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b
@@ -30,7 +31,7 @@ require (
3031
k8s.io/api v0.23.3
3132
k8s.io/apimachinery v0.23.3
3233
k8s.io/client-go v0.23.3
33-
k8s.io/klog v0.3.1
34+
k8s.io/klog/v2 v2.120.1
3435
)
3536

3637
require (
@@ -41,7 +42,7 @@ require (
4142
github.com/coreos/go-semver v0.3.0 // indirect
4243
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
4344
github.com/felixge/httpsnoop v1.0.4 // indirect
44-
github.com/go-logr/logr v1.3.0 // indirect
45+
github.com/go-logr/logr v1.4.1 // indirect
4546
github.com/go-logr/stdr v1.2.2 // indirect
4647
github.com/gogo/protobuf v1.3.2 // indirect
4748
github.com/golang/protobuf v1.5.3 // indirect
@@ -91,7 +92,6 @@ require (
9192
gopkg.in/inf.v0 v0.9.1 // indirect
9293
gopkg.in/yaml.v2 v2.4.0 // indirect
9394
gopkg.in/yaml.v3 v3.0.1 // indirect
94-
k8s.io/klog/v2 v2.30.0 // indirect
9595
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
9696
k8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect
9797
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect

go.sum

+5-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gubernator.go

+7-9
Original file line numberDiff line numberDiff line change
@@ -149,17 +149,15 @@ func (s *V1Instance) Close() (err error) {
149149
return nil
150150
}
151151

152-
if s.conf.Loader == nil {
153-
return nil
154-
}
155-
156152
s.global.Close()
157153

158-
err = s.workerPool.Store(ctx)
159-
if err != nil {
160-
s.log.WithError(err).
161-
Error("Error in workerPool.Store")
162-
return errors.Wrap(err, "Error in workerPool.Store")
154+
if s.conf.Loader != nil {
155+
err = s.workerPool.Store(ctx)
156+
if err != nil {
157+
s.log.WithError(err).
158+
Error("Error in workerPool.Store")
159+
return errors.Wrap(err, "Error in workerPool.Store")
160+
}
163161
}
164162

165163
err = s.workerPool.Close()

0 commit comments

Comments
 (0)