From 2b1fc4c9f8178a74c225dfdd27bc7e1cad6895f7 Mon Sep 17 00:00:00 2001 From: Vanja Pejovic Date: Fri, 17 Oct 2025 21:33:46 -0400 Subject: [PATCH 1/7] otelgrpc: optimize stats handler InPayload and OutPayload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was inspired by https://github.com/open-telemetry/opentelemetry-go-contrib/pull/7186 and profiles I noticed in my app. Also thanks to @boekkooi-impossiblecloud for writing the benchmarks. InPayload and OutPayload blocks would create a new attribute set for each message. This is particularly bad for streaming calls without thousands of messages. Creating the set once improves the speed and memory allocations of your benchmarks, as well as mine. ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc cpu: AMD Ryzen 9 7900X 12-Core Processor │ otelbase │ otelcached │ │ sec/op │ sec/op vs base │ ServerHandler_HandleRPC_Begin-24 42.30n ± 3% 42.58n ± 5% ~ (p=0.710 n=7) ServerHandler_HandleRPC_InPayload-24 463.1n ± 6% 351.2n ± 5% -24.16% (p=0.001 n=7) ServerHandler_HandleRPC_OutPayload-24 461.1n ± 2% 354.2n ± 9% -23.18% (p=0.001 n=7) ServerHandler_HandleRPC_OutTrailer-24 42.33n ± 2% 42.28n ± 5% ~ (p=0.710 n=7) ServerHandler_HandleRPC_OutHeader-24 231.7n ± 2% 230.7n ± 5% ~ (p=0.929 n=7) ServerHandler_HandleRPC_End-24 234.5n ± 6% 149.3n ± 1% -36.33% (p=0.001 n=7) ServerHandler_HandleRPC_Nil-24 41.91n ± 3% 41.79n ± 1% ~ (p=0.594 n=7) ServerHandler_HandleRPC_NoOp_Begin-24 42.59n ± 10% 42.30n ± 6% ~ (p=0.318 n=7) ServerHandler_HandleRPC_NoOp_InPayload-24 167.60n ± 6% 72.09n ± 8% -56.99% (p=0.001 n=7) ServerHandler_HandleRPC_NoOp_OutPayload-24 170.40n ± 3% 71.80n ± 1% -57.86% (p=0.001 n=7) ServerHandler_HandleRPC_NoOp_OutTrailer-24 42.30n ± 2% 42.19n ± 2% ~ (p=0.318 n=7) ServerHandler_HandleRPC_NoOp_OutHeader-24 44.60n ± 4% 43.85n ± 2% -1.68% (p=0.038 n=7) ServerHandler_HandleRPC_NoOp_End-24 230.1n ± 4% 147.7n ± 22% -35.81% (p=0.001 n=7) ServerHandler_HandleRPC_NoOp_Nil-24 41.69n ± 3% 41.76n ± 2% ~ (p=0.779 n=7) NoInstrumentation-24 985.8µ ± 11% 991.9µ ± 1% ~ (p=0.902 n=7) geomean 192.8n 156.1n -19.01% │ otelbase │ otelcached │ │ B/op │ B/op vs base │ ServerHandler_HandleRPC_Begin-24 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_InPayload-24 985.0 ± 0% 600.0 ± 0% -39.09% (p=0.001 n=7) ServerHandler_HandleRPC_OutPayload-24 985.0 ± 0% 600.0 ± 0% -39.09% (p=0.001 n=7) ServerHandler_HandleRPC_OutTrailer-24 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_OutHeader-24 297.0 ± 0% 297.0 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_End-24 576.0 ± 0% 224.0 ± 0% -61.11% (p=0.001 n=7) ServerHandler_HandleRPC_Nil-24 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_NoOp_Begin-24 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_NoOp_InPayload-24 432.00 ± 0% 48.00 ± 0% -88.89% (p=0.001 n=7) ServerHandler_HandleRPC_NoOp_OutPayload-24 432.00 ± 0% 48.00 ± 0% -88.89% (p=0.001 n=7) ServerHandler_HandleRPC_NoOp_OutTrailer-24 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_NoOp_OutHeader-24 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_NoOp_End-24 576.0 ± 0% 224.0 ± 0% -61.11% (p=0.001 n=7) ServerHandler_HandleRPC_NoOp_Nil-24 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=7) ¹ NoInstrumentation-24 2.805Mi ± 2% 2.796Mi ± 2% ~ (p=0.259 n=7) geomean 261.3 160.8 -38.44% ¹ all samples are equal │ otelbase │ otelcached │ │ allocs/op │ allocs/op vs base │ ServerHandler_HandleRPC_Begin-24 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_InPayload-24 9.000 ± 0% 7.000 ± 0% -22.22% (p=0.001 n=7) ServerHandler_HandleRPC_OutPayload-24 9.000 ± 0% 7.000 ± 0% -22.22% (p=0.001 n=7) ServerHandler_HandleRPC_OutTrailer-24 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_OutHeader-24 6.000 ± 0% 6.000 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_End-24 6.000 ± 0% 7.000 ± 0% +16.67% (p=0.001 n=7) ServerHandler_HandleRPC_Nil-24 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_NoOp_Begin-24 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_NoOp_InPayload-24 5.000 ± 0% 3.000 ± 0% -40.00% (p=0.001 n=7) ServerHandler_HandleRPC_NoOp_OutPayload-24 5.000 ± 0% 3.000 ± 0% -40.00% (p=0.001 n=7) ServerHandler_HandleRPC_NoOp_OutTrailer-24 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_NoOp_OutHeader-24 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=7) ¹ ServerHandler_HandleRPC_NoOp_End-24 6.000 ± 0% 7.000 ± 0% +16.67% (p=0.001 n=7) ServerHandler_HandleRPC_NoOp_Nil-24 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=7) ¹ NoInstrumentation-24 1.342k ± 3% 1.349k ± 2% ~ (p=0.519 n=7) geomean 5.310 4.898 -7.75% ¹ all samples are equal ``` --- .../grpc/otelgrpc/stats_handler.go | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go index 29d7ab2bdac..1eac0752431 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go @@ -28,7 +28,7 @@ type gRPCContextKey struct{} type gRPCContext struct { inMessages int64 outMessages int64 - metricAttrs []attribute.KeyValue + metricAttrs attribute.Set record bool } @@ -129,8 +129,11 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont ) } + // Make a new slice to avoid aliasing into the same attrs slice used by traces. + metricAttrs := make([]attribute.KeyValue, 0, len(attrs)+len(h.MetricAttributes)) + metricAttrs = append(append(metricAttrs, attrs...), h.MetricAttributes...) gctx := gRPCContext{ - metricAttrs: append(attrs, h.MetricAttributes...), + metricAttrs: attribute.NewSet(metricAttrs...), record: record, } @@ -227,8 +230,11 @@ func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont ) } + // Make a new slice to avoid aliasing into the same attrs slice used by traces. + metricAttrs := make([]attribute.KeyValue, 0, len(attrs)+len(h.MetricAttributes)) + metricAttrs = append(append(metricAttrs, attrs...), h.MetricAttributes...) gctx := gRPCContext{ - metricAttrs: append(attrs, h.MetricAttributes...), + metricAttrs: attribute.NewSet(metricAttrs...), record: record, } @@ -262,7 +268,7 @@ func (*clientHandler) HandleConn(context.Context, stats.ConnStats) { } type int64Hist interface { - Record(context.Context, int64, ...attribute.KeyValue) + RecordSet(context.Context, int64, attribute.Set) } func (c *config) handleRPC( @@ -286,7 +292,7 @@ func (c *config) handleRPC( case *stats.InPayload: if gctx != nil { messageId = atomic.AddInt64(&gctx.inMessages, 1) - inSize.Record(ctx, int64(rs.Length), gctx.metricAttrs...) + inSize.RecordSet(ctx, int64(rs.Length), gctx.metricAttrs) } if c.ReceivedEvent && span.IsRecording() { @@ -302,7 +308,7 @@ func (c *config) handleRPC( case *stats.OutPayload: if gctx != nil { messageId = atomic.AddInt64(&gctx.outMessages, 1) - outSize.Record(ctx, int64(rs.Length), gctx.metricAttrs...) + outSize.RecordSet(ctx, int64(rs.Length), gctx.metricAttrs) } if c.SentEvent && span.IsRecording() { @@ -341,14 +347,13 @@ func (c *config) handleRPC( span.End() } - var metricAttrs []attribute.KeyValue + // Allocate vararg slice once. + var recordOpts []metric.RecordOption if gctx != nil { - metricAttrs = make([]attribute.KeyValue, 0, len(gctx.metricAttrs)+1) - metricAttrs = append(metricAttrs, gctx.metricAttrs...) + recordOpts = []metric.RecordOption{metric.WithAttributeSet(gctx.metricAttrs), metric.WithAttributes(rpcStatusAttr)} + } else { + recordOpts = []metric.RecordOption{metric.WithAttributes(rpcStatusAttr)} } - metricAttrs = append(metricAttrs, rpcStatusAttr) - // Allocate vararg slice once. - recordOpts := []metric.RecordOption{metric.WithAttributeSet(attribute.NewSet(metricAttrs...))} // Use floating point division here for higher precision (instead of Millisecond method). // Measure right before calling Record() to capture as much elapsed time as possible. From c7a2b2dfd5fe97b3b2c6da039d4dfc329e644b8a Mon Sep 17 00:00:00 2001 From: Vanja Pejovic Date: Mon, 20 Oct 2025 10:35:18 -0400 Subject: [PATCH 2/7] flip slice allocation --- .../grpc/otelgrpc/stats_handler.go | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go index 1eac0752431..f632f9acd6f 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go @@ -111,9 +111,12 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont } if record { + // Make a new slice to avoid aliasing into the same attrs slice used by metrics. + spanAttributes := make([]attribute.KeyValue, 0, len(attrs)+len(h.SpanAttributes)) + spanAttributes = append(append(spanAttributes, attrs...), h.SpanAttributes...) opts := []trace.SpanStartOption{ trace.WithSpanKind(trace.SpanKindServer), - trace.WithAttributes(append(attrs, h.SpanAttributes...)...), + trace.WithAttributes(spanAttributes...), } if h.PublicEndpoint || (h.PublicEndpointFn != nil && h.PublicEndpointFn(ctx, info)) { opts = append(opts, trace.WithNewRoot()) @@ -129,11 +132,8 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont ) } - // Make a new slice to avoid aliasing into the same attrs slice used by traces. - metricAttrs := make([]attribute.KeyValue, 0, len(attrs)+len(h.MetricAttributes)) - metricAttrs = append(append(metricAttrs, attrs...), h.MetricAttributes...) gctx := gRPCContext{ - metricAttrs: attribute.NewSet(metricAttrs...), + metricAttrs: attribute.NewSet(append(attrs, h.MetricAttributes...)...), record: record, } @@ -222,19 +222,19 @@ func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont } if record { + // Make a new slice to avoid aliasing into the same attrs slice used by metrics. + spanAttributes := make([]attribute.KeyValue, 0, len(attrs)+len(h.SpanAttributes)) + spanAttributes = append(append(spanAttributes, attrs...), h.SpanAttributes...) ctx, _ = h.tracer.Start( ctx, name, trace.WithSpanKind(trace.SpanKindClient), - trace.WithAttributes(append(attrs, h.SpanAttributes...)...), + trace.WithAttributes(spanAttributes...), ) } - // Make a new slice to avoid aliasing into the same attrs slice used by traces. - metricAttrs := make([]attribute.KeyValue, 0, len(attrs)+len(h.MetricAttributes)) - metricAttrs = append(append(metricAttrs, attrs...), h.MetricAttributes...) gctx := gRPCContext{ - metricAttrs: attribute.NewSet(metricAttrs...), + metricAttrs: attribute.NewSet(append(attrs, h.MetricAttributes...)...), record: record, } From dc54330974c95bc80b45ecc968345f9100fd7224 Mon Sep 17 00:00:00 2001 From: Vanja Pejovic Date: Mon, 20 Oct 2025 11:59:29 -0400 Subject: [PATCH 3/7] don't use cached set for End block --- .../grpc/otelgrpc/stats_handler.go | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go index f632f9acd6f..3bc0833e454 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go @@ -26,10 +26,11 @@ import ( type gRPCContextKey struct{} type gRPCContext struct { - inMessages int64 - outMessages int64 - metricAttrs attribute.Set - record bool + inMessages int64 + outMessages int64 + metricAttrs []attribute.KeyValue + metricAttrSet attribute.Set + record bool } type serverHandler struct { @@ -133,9 +134,10 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont } gctx := gRPCContext{ - metricAttrs: attribute.NewSet(append(attrs, h.MetricAttributes...)...), + metricAttrs: append(attrs, h.MetricAttributes...), record: record, } + gctx.metricAttrSet = attribute.NewSet(gctx.metricAttrs...) return context.WithValue(ctx, gRPCContextKey{}, &gctx) } @@ -234,9 +236,10 @@ func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont } gctx := gRPCContext{ - metricAttrs: attribute.NewSet(append(attrs, h.MetricAttributes...)...), + metricAttrs: append(attrs, h.MetricAttributes...), record: record, } + gctx.metricAttrSet = attribute.NewSet(gctx.metricAttrs...) return inject(context.WithValue(ctx, gRPCContextKey{}, &gctx), h.Propagators) } @@ -292,7 +295,7 @@ func (c *config) handleRPC( case *stats.InPayload: if gctx != nil { messageId = atomic.AddInt64(&gctx.inMessages, 1) - inSize.RecordSet(ctx, int64(rs.Length), gctx.metricAttrs) + inSize.RecordSet(ctx, int64(rs.Length), gctx.metricAttrSet) } if c.ReceivedEvent && span.IsRecording() { @@ -308,7 +311,7 @@ func (c *config) handleRPC( case *stats.OutPayload: if gctx != nil { messageId = atomic.AddInt64(&gctx.outMessages, 1) - outSize.RecordSet(ctx, int64(rs.Length), gctx.metricAttrs) + outSize.RecordSet(ctx, int64(rs.Length), gctx.metricAttrSet) } if c.SentEvent && span.IsRecording() { @@ -347,14 +350,18 @@ func (c *config) handleRPC( span.End() } - // Allocate vararg slice once. - var recordOpts []metric.RecordOption + var metricAttrs []attribute.KeyValue if gctx != nil { - recordOpts = []metric.RecordOption{metric.WithAttributeSet(gctx.metricAttrs), metric.WithAttributes(rpcStatusAttr)} - } else { - recordOpts = []metric.RecordOption{metric.WithAttributes(rpcStatusAttr)} + // Don't use gctx.metricAttrSet here, because it requires passing + // multiple RecordOptions, which would call metric.mergeSets and + // allocate a new set for each Record call. + metricAttrs = make([]attribute.KeyValue, 0, len(gctx.metricAttrs)+1) + metricAttrs = append(metricAttrs, gctx.metricAttrs...) } - + metricAttrs = append(metricAttrs, rpcStatusAttr) + // Allocate vararg slice once. + // recordOpts := []metric.RecordOption{metric.WithAttributeSet(attribute.NewSet(metricAttrs...))} + recordOpts := []metric.RecordOption{metric.WithAttributeSet(attribute.NewSet(metricAttrs...))} // Use floating point division here for higher precision (instead of Millisecond method). // Measure right before calling Record() to capture as much elapsed time as possible. elapsedTime := float64(rs.EndTime.Sub(rs.BeginTime)) / float64(time.Millisecond) From 913637ea2a581da1e0428a1de69c54c348d33e7e Mon Sep 17 00:00:00 2001 From: Vanja Pejovic Date: Mon, 20 Oct 2025 12:00:03 -0400 Subject: [PATCH 4/7] fix whitespace --- .../google.golang.org/grpc/otelgrpc/stats_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go index 3bc0833e454..bbc2bdd84df 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go @@ -360,8 +360,8 @@ func (c *config) handleRPC( } metricAttrs = append(metricAttrs, rpcStatusAttr) // Allocate vararg slice once. - // recordOpts := []metric.RecordOption{metric.WithAttributeSet(attribute.NewSet(metricAttrs...))} recordOpts := []metric.RecordOption{metric.WithAttributeSet(attribute.NewSet(metricAttrs...))} + // Use floating point division here for higher precision (instead of Millisecond method). // Measure right before calling Record() to capture as much elapsed time as possible. elapsedTime := float64(rs.EndTime.Sub(rs.BeginTime)) / float64(time.Millisecond) From 4f2dc4206ebbba417008e9c932b2e2fb63a73952 Mon Sep 17 00:00:00 2001 From: Vanja Pejovic Date: Mon, 20 Oct 2025 15:24:54 -0400 Subject: [PATCH 5/7] use meter in benchmarks --- .../grpc/otelgrpc/stats_handler_bench_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler_bench_test.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler_bench_test.go index 1b3b04109b7..56bbc39390c 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler_bench_test.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler_bench_test.go @@ -10,6 +10,7 @@ import ( "time" metricnoop "go.opentelemetry.io/otel/metric/noop" + "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/trace" tracenoop "go.opentelemetry.io/otel/trace/noop" "google.golang.org/grpc/peer" @@ -29,7 +30,7 @@ func benchmarkServerHandlerHandleRPC(b *testing.B, stat stats.RPCStats) { WithTracerProvider(trace.NewTracerProvider( trace.WithSampler(trace.AlwaysSample()), )), - WithMeterProvider(metricnoop.NewMeterProvider()), + WithMeterProvider(metric.NewMeterProvider()), WithMessageEvents(ReceivedEvents, SentEvents), ) ctx := b.Context() From 5873c7f727e6f46cf8b27e19f793264450dd3888 Mon Sep 17 00:00:00 2001 From: Vanja Pejovic Date: Mon, 20 Oct 2025 15:25:22 -0400 Subject: [PATCH 6/7] don't convert to interface on each handleRPC call --- .../google.golang.org/grpc/otelgrpc/stats_handler.go | 8 ++++---- .../grpc/otelgrpc/stats_handler_test.go | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go index bbc2bdd84df..278f6d0d99e 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go @@ -39,8 +39,8 @@ type serverHandler struct { tracer trace.Tracer duration rpcconv.ServerDuration - inSize rpcconv.ServerRequestSize - outSize rpcconv.ServerResponseSize + inSize int64Hist + outSize int64Hist inMsg rpcconv.ServerRequestsPerRPC outMsg rpcconv.ServerResponsesPerRPC } @@ -162,8 +162,8 @@ type clientHandler struct { tracer trace.Tracer duration rpcconv.ClientDuration - inSize rpcconv.ClientResponseSize - outSize rpcconv.ClientRequestSize + inSize int64Hist + outSize int64Hist inMsg rpcconv.ClientResponsesPerRPC outMsg rpcconv.ClientRequestsPerRPC } diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler_test.go b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler_test.go index b67b44c75fd..3424247b796 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler_test.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/embedded" "go.opentelemetry.io/otel/propagation" @@ -178,8 +179,8 @@ func TestNilInstruments(t *testing.T) { h := hIface.(*serverHandler) assert.NotPanics(t, func() { h.duration.Record(ctx, 0) }, "duration") - assert.NotPanics(t, func() { h.inSize.Record(ctx, 0) }, "inSize") - assert.NotPanics(t, func() { h.outSize.Record(ctx, 0) }, "outSize") + assert.NotPanics(t, func() { h.inSize.RecordSet(ctx, 0, *attribute.EmptySet()) }, "inSize") + assert.NotPanics(t, func() { h.outSize.RecordSet(ctx, 0, *attribute.EmptySet()) }, "outSize") assert.NotPanics(t, func() { h.inMsg.Record(ctx, 0) }, "inMsg") assert.NotPanics(t, func() { h.outMsg.Record(ctx, 0) }, "outMsg") }) @@ -192,8 +193,8 @@ func TestNilInstruments(t *testing.T) { h := hIface.(*clientHandler) assert.NotPanics(t, func() { h.duration.Record(ctx, 0) }, "duration") - assert.NotPanics(t, func() { h.inSize.Record(ctx, 0) }, "inSize") - assert.NotPanics(t, func() { h.outSize.Record(ctx, 0) }, "outSize") + assert.NotPanics(t, func() { h.inSize.RecordSet(ctx, 0, *attribute.EmptySet()) }, "inSize") + assert.NotPanics(t, func() { h.outSize.RecordSet(ctx, 0, *attribute.EmptySet()) }, "outSize") assert.NotPanics(t, func() { h.inMsg.Record(ctx, 0) }, "inMsg") assert.NotPanics(t, func() { h.outMsg.Record(ctx, 0) }, "outMsg") }) From 60cba14273035eef3ce7ca60eab85306703e3b6a Mon Sep 17 00:00:00 2001 From: Vanja Pejovic Date: Mon, 20 Oct 2025 16:47:28 -0400 Subject: [PATCH 7/7] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a4d53b5827..fc97723c76c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `ParseYAML` in `go.opentelemetry.io/contrib/otelconf` now supports environment variables substitution in the format `${[env:]VAR_NAME[:-defaultvalue]}`. (#6215) +### Changed + +- Improve performance by reducing allocations in the gRPC stats handler in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#8035) + ### Removed - Drop support for [Go 1.23]. (#7831)