Skip to content

Commit 240f763

Browse files
authored
fix(transport): fix memory leak in grpc.Dial (#2329)
* Memoize otelgrpc.NewClientHandler for reuse fixes: #2321
1 parent 03de26a commit 240f763

File tree

2 files changed

+24
-2
lines changed

2 files changed

+24
-2
lines changed

transport/grpc/dial.go

+23-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"net"
1515
"os"
1616
"strings"
17+
"sync"
1718
"time"
1819

1920
"cloud.google.com/go/compute/metadata"
@@ -27,6 +28,7 @@ import (
2728
grpcgoogle "google.golang.org/grpc/credentials/google"
2829
grpcinsecure "google.golang.org/grpc/credentials/insecure"
2930
"google.golang.org/grpc/credentials/oauth"
31+
"google.golang.org/grpc/stats"
3032

3133
// Install grpclb, which is required for direct path.
3234
_ "google.golang.org/grpc/balancer/grpclb"
@@ -47,6 +49,26 @@ var logRateLimiter = rate.Sometimes{Interval: 1 * time.Second}
4749
// Assign to var for unit test replacement
4850
var dialContext = grpc.DialContext
4951

52+
// otelStatsHandler is a singleton otelgrpc.clientHandler to be used across
53+
// all dial connections to avoid the memory leak documented in
54+
// https://github.com/open-telemetry/opentelemetry-go-contrib/issues/4226
55+
//
56+
// TODO: If 4226 has been fixed in opentelemetry-go-contrib, replace this
57+
// singleton with inline usage for simplicity.
58+
var (
59+
initOtelStatsHandlerOnce sync.Once
60+
otelStatsHandler stats.Handler
61+
)
62+
63+
// otelGRPCStatsHandler returns singleton otelStatsHandler for reuse across all
64+
// dial connections.
65+
func otelGRPCStatsHandler() stats.Handler {
66+
initOtelStatsHandlerOnce.Do(func() {
67+
otelStatsHandler = otelgrpc.NewClientHandler()
68+
})
69+
return otelStatsHandler
70+
}
71+
5072
// Dial returns a GRPC connection for use communicating with a Google cloud
5173
// service, configured with the given ClientOptions.
5274
func Dial(ctx context.Context, opts ...option.ClientOption) (*grpc.ClientConn, error) {
@@ -219,7 +241,7 @@ func addOpenTelemetryStatsHandler(opts []grpc.DialOption, settings *internal.Dia
219241
if settings.TelemetryDisabled {
220242
return opts
221243
}
222-
return append(opts, grpc.WithStatsHandler(otelgrpc.NewClientHandler()))
244+
return append(opts, grpc.WithStatsHandler(otelGRPCStatsHandler()))
223245
}
224246

225247
// grpcTokenSource supplies PerRPCCredentials from an oauth.TokenSource.

transport/grpc/dial_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func TestLogDirectPathMisconfigNotOnGCE(t *testing.T) {
136136
logDirectPathMisconfig(endpoint, creds.TokenSource, o)
137137

138138
if !metadata.OnGCE() {
139-
wantedLog := "WARNING: DirectPath is misconfigured. DirectPath is only available in a GCE environment.."
139+
wantedLog := "WARNING: DirectPath is misconfigured. DirectPath is only available in a GCE environment."
140140
if !strings.Contains(buf.String(), wantedLog) {
141141
t.Fatalf("got: %v, want: %v", buf.String(), wantedLog)
142142
}

0 commit comments

Comments
 (0)