Skip to content

Commit

Permalink
[v13] Reduce memory leakage in API client caused by otelgrpc interc…
Browse files Browse the repository at this point in the history
…eptors (#30991)

* Avoid memory leaking in `otelgrpc` interceptors

* Fix for non go 1.21 usage

* Add link to issue reported in otel-go-contrib
  • Loading branch information
strideynet authored Aug 25, 2023
1 parent 9732c04 commit f2f8a96
Showing 1 changed file with 32 additions and 2 deletions.
34 changes: 32 additions & 2 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,12 +451,12 @@ func (c *Client) dialGRPC(ctx context.Context, addr string) error {
dialOpts = append(dialOpts, grpc.WithContextDialer(c.grpcDialer()))
dialOpts = append(dialOpts,
grpc.WithChainUnaryInterceptor(
otelgrpc.UnaryClientInterceptor(),
otelUnaryClientInterceptor(),
metadata.UnaryClientInterceptor,
breaker.UnaryClientInterceptor(cb),
),
grpc.WithChainStreamInterceptor(
otelgrpc.StreamClientInterceptor(),
otelStreamClientInterceptor(),
metadata.StreamClientInterceptor,
breaker.StreamClientInterceptor(cb),
),
Expand Down Expand Up @@ -487,6 +487,36 @@ func (c *Client) dialGRPC(ctx context.Context, addr string) error {
return nil
}

// TODO(noah): Once we upgrade to go 1.21, change invocations of this to
// sync.OnceValue
func onceValue[T any](f func() T) func() T {
var (
value T
once sync.Once
)
return func() T {
once.Do(func() {
value = f()
})
return value
}
}

// We wrap the creation of the otelgrpc interceptors in a sync.Once - this is
// because each time this is called, they create a new underlying metric. If
// something (e.g tbot) is repeatedly creating new clients and closing them,
// then this leads to a memory leak since the underlying metric is not cleaned
// up.
// See https://github.com/gravitational/teleport/issues/30759
// See https://github.com/open-telemetry/opentelemetry-go-contrib/issues/4226
var otelStreamClientInterceptor = onceValue(func() grpc.StreamClientInterceptor {
return otelgrpc.StreamClientInterceptor()
})

var otelUnaryClientInterceptor = onceValue(func() grpc.UnaryClientInterceptor {
return otelgrpc.UnaryClientInterceptor()
})

// ConfigureALPN configures ALPN SNI cluster routing information in TLS settings allowing for
// allowing to dial auth service through Teleport Proxy directly without using SSH Tunnels.
func ConfigureALPN(tlsConfig *tls.Config, clusterName string) *tls.Config {
Expand Down

0 comments on commit f2f8a96

Please sign in to comment.