From d0ab65a178474671735bf4c5fc9daab44f27fe4a Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Tue, 9 Jul 2019 11:08:59 +0200 Subject: [PATCH] cmd/stcrashreceiver: Don't leak clients Use a global raven.Client because they allocate an http.Client for each, with a separate CA bundle and infinite connection idle time. Infinite connection idle time means that if the client is never used again it will always keep the connection around, not verifying whether it's closed server side or not. This leaks about a megabyte of memory for each client every created. client.Close() doesn't help with this because the http.Client is still around, retained by its own goroutines. The thing with the map is just to retain the API on sendReport, even though there will in practice only ever be one DSN per process instance... --- cmd/stcrashreceiver/sentry.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/cmd/stcrashreceiver/sentry.go b/cmd/stcrashreceiver/sentry.go index b76759dbf..2480b3cf5 100644 --- a/cmd/stcrashreceiver/sentry.go +++ b/cmd/stcrashreceiver/sentry.go @@ -12,6 +12,7 @@ import ( "io/ioutil" "regexp" "strings" + "sync" raven "github.com/getsentry/raven-go" "github.com/maruel/panicparse/stack" @@ -25,15 +26,27 @@ func init() { raven.SetSourceCodeLoader(loader) } +var ( + clients = make(map[string]*raven.Client) + clientsMut sync.Mutex +) + func sendReport(dsn, path string, report []byte) error { pkt, err := parseReport(path, report) if err != nil { return err } - cli, err := raven.New(dsn) - if err != nil { - return err + clientsMut.Lock() + defer clientsMut.Unlock() + + cli, ok := clients[dsn] + if !ok { + cli, err = raven.New(dsn) + if err != nil { + return err + } + clients[dsn] = cli } // The client sets release and such on the packet before sending, in the @@ -42,6 +55,7 @@ func sendReport(dsn, path string, report []byte) error { cli.SetRelease(pkt.Release) cli.SetEnvironment(pkt.Environment) + defer cli.Wait() _, errC := cli.Capture(pkt, nil) return <-errC }