Files
59d848a020 [pdata] Use pool-aware constructors in gRPC service handlers (#14763)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description

The gRPC service handlers for all four signal types allocate the
top-level ExportXxxServiceRequest with bare new(), bypassing the
sync.Pool when pdata.useProtoPooling is enabled. This means objects
returned to the pool via Delete are never retrieved, so the pool
accumulates unreused objects.

Replace new() with the pool-aware New*() constructors so the top-level
request struct participates in the pool lifecycle.

benchstat (20 runs, pdata.useProtoPooling=true, 10k logs/request):

```
                             │   old (new())   │  new (New*())    │
                             │     sec/op      │  sec/op  vs base │
GRPCLogsSequentialPooling-12     3.858m ± 2%   3.811m ± 1%  ~ (p=0.165 n=20)
                             │      B/op       │   B/op   vs base │
GRPCLogsSequentialPooling-12    3.551Mi ± 2%  3.523Mi ± 1%  ~ (p=0.445 n=20)
                             │   allocs/op     │ allocs/op vs base│
GRPCLogsSequentialPooling-12     75.46k ± 0%   75.46k ± 0%  ~ (p=0.867 n=20)
```

No measurable perf difference (1 alloc out of 75k), but this is a
correctness fix for the pool lifecycle.


<!-- Issue number if applicable -->
#### Link to tracking issue
Fixes #

<!--Describe what testing was performed and which tests were added.-->
#### Testing

<!--Describe the documentation added.-->
#### Documentation

<!--Please delete paragraphs that you did not use before submitting.-->

---------

Co-authored-by: Pablo Baeyens <[email protected]>
2026-04-22 09:53:43 +00:00

89 lines
3.0 KiB
Go

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package otelgrpc // import "go.opentelemetry.io/collector/pdata/internal/otelgrpc"
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"go.opentelemetry.io/collector/pdata/internal"
)
// TraceServiceClient is the client API for TraceService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type TraceServiceClient interface {
Export(context.Context, *internal.ExportTraceServiceRequest, ...grpc.CallOption) (*internal.ExportTraceServiceResponse, error)
}
type traceServiceClient struct {
cc *grpc.ClientConn
}
func NewTraceServiceClient(cc *grpc.ClientConn) TraceServiceClient {
return &traceServiceClient{cc}
}
func (c *traceServiceClient) Export(ctx context.Context, in *internal.ExportTraceServiceRequest, opts ...grpc.CallOption) (*internal.ExportTraceServiceResponse, error) {
out := new(internal.ExportTraceServiceResponse)
err := c.cc.Invoke(ctx, "/opentelemetry.proto.collector.trace.v1.TraceService/Export", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// TraceServiceServer is the server API for TraceService service.
type TraceServiceServer interface {
Export(context.Context, *internal.ExportTraceServiceRequest) (*internal.ExportTraceServiceResponse, error)
}
// UnimplementedTraceServiceServer can be embedded to have forward compatible implementations.
type UnimplementedTraceServiceServer struct{}
func (*UnimplementedTraceServiceServer) Export(context.Context, *internal.ExportTraceServiceRequest) (*internal.ExportTraceServiceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Export not implemented")
}
func RegisterTraceServiceServer(s *grpc.Server, srv TraceServiceServer) {
s.RegisterService(&traceServiceServiceDesc, srv)
}
// Context cannot be the first parameter of the function because gRPC definition.
//
//nolint:revive
func traceServiceExportHandler(srv any, ctx context.Context, dec func(any) error, interceptor grpc.UnaryServerInterceptor) (any, error) {
in := internal.NewExportTraceServiceRequest()
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TraceServiceServer).Export(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/opentelemetry.proto.collector.trace.v1.TraceService/Export",
}
handler := func(ctx context.Context, req any) (any, error) {
return srv.(TraceServiceServer).Export(ctx, req.(*internal.ExportTraceServiceRequest))
}
return interceptor(ctx, in, info, handler)
}
var traceServiceServiceDesc = grpc.ServiceDesc{
ServiceName: "opentelemetry.proto.collector.trace.v1.TraceService",
HandlerType: (*TraceServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Export",
Handler: traceServiceExportHandler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "opentelemetry/proto/collector/trace/v1/trace_service.proto",
}