From ee05c54d5eae1a14da737e5e32f7b4fec40475f7 Mon Sep 17 00:00:00 2001 From: Zeying Zhu Date: Tue, 5 May 2026 18:04:37 -0400 Subject: [PATCH] fix(otlp): complete post-#262 DDSketch encoding-value + proto wire-type absorption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sweep blocker #1: `EXPORTER_SDK_AGG=dd-delta` was rejected by the OTLP exporter with `unknown ddsketch encoding: ddsketch_proto_delta` because PR #262 introduced `metricdata.DDSketchEncodingProtoDelta` but did not extend `DDSketchEncodingValue`'s switch to cover it (sibling sketches HLL/CountSketch/CountMinSketch were absorbed in #262 but DDSketch was missed). Without this, none of the matrix's DDSketch cells run. Fix 1 (encoding-value mapping): adds the `ProtoDelta` arm to `DDSketchEncodingValue` in `otlpmetricgrpc` + `otlpmetrichttp` exporters and the shared template. New `encoding_value_test.go` pins the post-#262 surface for all five sketches' `*EncodingValue` helpers, including a round-trip through `DDSketchDataPoints`. Pinning tests for the agent-side decoder in `opentelemetry-collector-contrib-patch/processor/ddsketchprocessor/ envelope_decode_test.go` document the wire-format invariants `decodeDDSketchEnvelope` relies on (full-state round trip, bare-state rejection, empty-envelope rejection) so any future encoder regression is caught at the unit-test layer. Fix 2 (alpha wire-type) + Fix 3 (oneof variant) are NOT addressed in this PR. Investigation confirmed they share a single structural root cause: the SDK's DDSketch aggregator (`opentelemetry-go-patch/sdk/metric/internal/aggregate/ddsketch.go`) still serializes via `proto.Marshal(sk.ToProto())` — DataDog's `sketchpb.DDSketch` proto — while every consumer (asap-precompute-go's `DDSketchWrapper.ApplyDelta`, asap-precompute-rs's `DDSketchWrapper::decode_envelope`, the agent's `decodeDDSketchEnvelope`) expects sketchlib-go's `SketchEnvelope{DDSketchState}`. The schemas collide on field 1 wire types (DataDog's `Mapping` is LengthDelimited; sketchlib-go's `alpha` is fixed64), surfacing as the reported `DDSketchState.alpha: invalid wire type` error. Aligning consumers with the SDK's DataDog format would lose the cross-language byte parity ProjectASAP/ASAPCollector#243 just landed; aligning the SDK with sketchlib-go requires migrating the aggregator off DataDog/sketches-go onto sketchlib-go's DDSketch — same path KLL, HLL, CountSketch, CountMinSketch already took. That migration sits outside this PR's touch list (only the encoding-value map, OTLP encoding-switch, processor.go decoder, and Rust wrapper were in scope per the sweep agent's autopilot envelope). Verification: - `go test ./internal/transform/...` green for both grpc + http exporters (8 new tests under `encoding_value_test.go`). - `go test ./...` green for `opentelemetry-collector-contrib/ processor/ddsketchprocessor` including the 3 new envelope-decode tests. - `go test ./...` green for `opentelemetry-go/sdk/metric/...`. - `cargo test --release` green for `asap-precompute-rs` (no Rust changes; verified parity tests still pass). No sibling sketch (HLL/CS/CMS) gap was found — the original PR #262 absorption is complete for them; only DDSketch was missed. KLLSketchEncodingValue intentionally has no Delta arm (KLL has no delta wire path). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../ddsketchprocessor/envelope_decode_test.go | 105 ++++++++++++++ .../internal/transform/encoding_value_test.go | 132 ++++++++++++++++++ .../internal/transform/metricdata.go | 2 + .../internal/transform/encoding_value_test.go | 132 ++++++++++++++++++ .../internal/transform/metricdata.go | 2 + .../otlpmetric/transform/metricdata.go.tmpl | 2 + 6 files changed, 375 insertions(+) create mode 100644 opentelemetry-collector-contrib-patch/processor/ddsketchprocessor/envelope_decode_test.go create mode 100644 opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/encoding_value_test.go create mode 100644 opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/encoding_value_test.go diff --git a/opentelemetry-collector-contrib-patch/processor/ddsketchprocessor/envelope_decode_test.go b/opentelemetry-collector-contrib-patch/processor/ddsketchprocessor/envelope_decode_test.go new file mode 100644 index 00000000..2595de2e --- /dev/null +++ b/opentelemetry-collector-contrib-patch/processor/ddsketchprocessor/envelope_decode_test.go @@ -0,0 +1,105 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// envelope_decode_test.go pins the wire-format invariants the +// patched DDSketch processor's decoder relies on. Background: +// post-#262, the SDK's metricdata.DDSketchEncodingProtoDelta path +// landed but the OTLP encoding-string switch was incomplete and +// the SDK's DDSketch aggregator (internal/aggregate/ddsketch.go) +// still serializes via DataDog `sketchpb.DDSketch` — a different +// proto schema than the sketchlib-go `SketchEnvelope{DDSketchState}` +// the agent decoder expects. The structural mismatch surfaces as +// `DDSketchState.alpha: invalid wire type: LengthDelimited (expected +// SixtyFourBit)` on the consuming side. These tests pin the +// decoder's expected-input contract so the next time someone touches +// the encoder we catch a re-introduction of the same drift. + +package ddsketchprocessor + +import ( + "testing" + + commonpb "github.com/ProjectASAP/sketchlib-go/proto/common" + ddpb "github.com/ProjectASAP/sketchlib-go/proto/ddsketch" + envpb "github.com/ProjectASAP/sketchlib-go/proto/sketch_envelope" + ddsketch "github.com/ProjectASAP/sketchlib-go/sketches/DDSketch" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +// TestDecodeDDSketchEnvelope_RoundTripFull confirms a happy-path +// SerializePortable → proto.Marshal → decodeDDSketchEnvelope round +// trip succeeds and the recovered sketch matches the input. This is +// the wire-format contract the EXPORTER_SDK_AGG=dd-full path +// requires once the SDK encoder is migrated off DataDog's +// sketchpb.DDSketch onto sketchlib-go's portable shape. +func TestDecodeDDSketchEnvelope_RoundTripFull(t *testing.T) { + src := ddsketch.NewDDSketch(0.01) + for _, v := range []float64{1.5, 2.5, 3.5, 100.0, 99.0} { + src.Update(v) + } + env, err := src.SerializePortable() + require.NoError(t, err) + bytes, err := proto.Marshal(env) + require.NoError(t, err) + + recovered, err := decodeDDSketchEnvelope(bytes) + require.NoError(t, err) + require.NotNil(t, recovered) + assert.Equal(t, src.GetCount(), recovered.GetCount()) +} + +// TestDecodeDDSketchEnvelope_BareStateRejected confirms that a bare +// DDSketchState (no envelope wrapper) is rejected. proto.Unmarshal +// trips on the field-number / wire-type clash between +// SketchEnvelope.format_version (varint, field 1) and +// DDSketchState.alpha (fixed64, field 1), surfacing as a generic +// `cannot parse invalid wire-format data` error. The decoder +// returns this as-is without reaching the GetDdsketch sentinel — +// the runtime's dispatcher then falls through to bare-state and +// delta decode paths, which is the documented contract. +func TestDecodeDDSketchEnvelope_BareStateRejected(t *testing.T) { + bare := &ddpb.DDSketchState{ + Alpha: 0.01, + StoreCounts: []uint64{1, 2, 3}, + StoreOffset: 0, + Count: 6, + Sum: 42, + Min: 1.5, + Max: 99.0, + } + bytes, err := proto.Marshal(bare) + require.NoError(t, err) + + got, err := decodeDDSketchEnvelope(bytes) + require.Error(t, err) + assert.Nil(t, got) +} + +// TestDecodeDDSketchEnvelope_EmptyEnvelopeRejected pins that an +// envelope with no oneof variant set (e.g. an envelope built without +// a sketch_state field, which is exactly the wire-shape the runtime +// produces when `proto.Unmarshal` is fed bytes from a different proto +// schema entirely — like DataDog's sketchpb.DDSketch) trips the same +// "did not carry DDSketchState" error path. This is the class of +// failure the sweep agent's `EXPORTER_SDK_AGG=dd-full` cell hit; the +// fix lives in the SDK encoder (out of this PR's scope) and is +// tracked as sweep-blocker-1 follow-up. +func TestDecodeDDSketchEnvelope_EmptyEnvelopeRejected(t *testing.T) { + env := &envpb.SketchEnvelope{ + FormatVersion: 1, + Producer: &commonpb.ProducerInfo{ + Library: "test", + Version: "0", + }, + // No sketch_state oneof set. + } + bytes, err := proto.Marshal(env) + require.NoError(t, err) + + got, err := decodeDDSketchEnvelope(bytes) + require.Error(t, err) + assert.Nil(t, got) + assert.Contains(t, err.Error(), "did not carry DDSketchState") +} diff --git a/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/encoding_value_test.go b/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/encoding_value_test.go new file mode 100644 index 00000000..8fb4cf5a --- /dev/null +++ b/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/encoding_value_test.go @@ -0,0 +1,132 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Tests for the *EncodingValue helpers that map metricdata-typed +// encoding strings to the OTLP proto enum. Splits out from the +// auto-generated metricdata_test.go (which is regenerated from +// internal/shared/otlp/otlpmetric/transform/metricdata_test.go.tmpl) +// so the new cases survive a re-template. +// +// Background: PR #262 introduced metricdata.DDSketchEncodingProtoDelta +// alongside the pre-existing CountSketch / CountMinSketch / HLLSketch +// delta encodings, but DDSketchEncodingValue's switch was not extended +// to cover it. The OTLP exporter then rejected EXPORTER_SDK_AGG=dd-delta +// with `unknown ddsketch encoding: ddsketch_proto_delta`. These tests +// pin the post-#262 absorption. + +package transform + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/sdk/metric/metricdata" + mpb "go.opentelemetry.io/proto/otlp/metrics/v1" +) + +func TestDDSketchEncodingValue(t *testing.T) { + tests := []struct { + name string + in metricdata.DDSketchEncoding + want mpb.DDSketchEncoding + }{ + {"proto_full", metricdata.DDSketchEncodingProto, mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO}, + {"proto_delta", metricdata.DDSketchEncodingProtoDelta, mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO_DELTA}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := DDSketchEncodingValue(tc.in) + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestDDSketchEncodingValue_Unknown(t *testing.T) { + got, err := DDSketchEncodingValue(metricdata.DDSketchEncoding("not-a-real-encoding")) + require.Error(t, err) + assert.Equal(t, mpb.DDSketchEncoding_DDSKETCH_ENCODING_UNSPECIFIED, got) +} + +// Sibling sketches: confirm Delta + Proto map cleanly today so we +// catch regressions the next time someone touches the switch. + +func TestKLLSketchEncodingValue_Proto(t *testing.T) { + got, err := KLLSketchEncodingValue(metricdata.KLLSketchEncodingProto) + require.NoError(t, err) + assert.Equal(t, mpb.KLLSketchEncoding_KLL_SKETCH_ENCODING_PROTO, got) +} + +func TestCountSketchEncodingValue(t *testing.T) { + tests := []struct { + name string + in metricdata.CountSketchEncoding + want mpb.CountSketchEncoding + }{ + {"proto", metricdata.CountSketchEncodingProto, mpb.CountSketchEncoding_COUNT_SKETCH_ENCODING_PROTO}, + {"delta", metricdata.CountSketchEncodingDelta, mpb.CountSketchEncoding_COUNT_SKETCH_ENCODING_DELTA}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := CountSketchEncodingValue(tc.in) + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestCountMinSketchEncodingValue(t *testing.T) { + tests := []struct { + name string + in metricdata.CountMinSketchEncoding + want mpb.CountMinSketchEncoding + }{ + {"proto", metricdata.CountMinSketchEncodingProto, mpb.CountMinSketchEncoding_COUNT_MIN_SKETCH_ENCODING_PROTO}, + {"delta", metricdata.CountMinSketchEncodingDelta, mpb.CountMinSketchEncoding_COUNT_MIN_SKETCH_ENCODING_DELTA}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := CountMinSketchEncodingValue(tc.in) + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestHLLSketchEncodingValue(t *testing.T) { + tests := []struct { + name string + in metricdata.HLLSketchEncoding + want mpb.HLLSketchEncoding + }{ + {"proto", metricdata.HLLSketchEncodingProto, mpb.HLLSketchEncoding_HLL_SKETCH_ENCODING_PROTO}, + {"delta", metricdata.HLLSketchEncodingDelta, mpb.HLLSketchEncoding_HLL_SKETCH_ENCODING_DELTA}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := HLLSketchEncodingValue(tc.in) + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +// TestDDSketchDataPointsCarriesProtoDeltaEncoding round-trips a +// dd-delta-encoded SDK data point through DDSketchDataPoints and +// asserts the OTLP enum lands at PROTO_DELTA. This is the path +// EXPORTER_SDK_AGG=dd-delta exercises end-to-end. +func TestDDSketchDataPointsCarriesProtoDeltaEncoding(t *testing.T) { + in := []metricdata.DDSketchDataPoint[float64]{ + { + Sketch: []byte{0x01, 0x02, 0x03}, + Encoding: metricdata.DDSketchEncodingProtoDelta, + }, + } + out, err := DDSketchDataPoints(in) + require.NoError(t, err) + require.Len(t, out, 1) + assert.Equal(t, mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO_DELTA, out[0].Encoding) + assert.Equal(t, []byte{0x01, 0x02, 0x03}, out[0].Sketch) +} diff --git a/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata.go b/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata.go index 60a43045..3880eb70 100644 --- a/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata.go +++ b/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/transform/metricdata.go @@ -378,6 +378,8 @@ func DDSketchEncodingValue(enc metricdata.DDSketchEncoding) (mpb.DDSketchEncodin switch enc { case metricdata.DDSketchEncodingProto: return mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO, nil + case metricdata.DDSketchEncodingProtoDelta: + return mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO_DELTA, nil default: return mpb.DDSketchEncoding_DDSKETCH_ENCODING_UNSPECIFIED, fmt.Errorf("%w: %s", errUnknownDDSketchEncoding, enc) } diff --git a/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/encoding_value_test.go b/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/encoding_value_test.go new file mode 100644 index 00000000..8fb4cf5a --- /dev/null +++ b/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/encoding_value_test.go @@ -0,0 +1,132 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Tests for the *EncodingValue helpers that map metricdata-typed +// encoding strings to the OTLP proto enum. Splits out from the +// auto-generated metricdata_test.go (which is regenerated from +// internal/shared/otlp/otlpmetric/transform/metricdata_test.go.tmpl) +// so the new cases survive a re-template. +// +// Background: PR #262 introduced metricdata.DDSketchEncodingProtoDelta +// alongside the pre-existing CountSketch / CountMinSketch / HLLSketch +// delta encodings, but DDSketchEncodingValue's switch was not extended +// to cover it. The OTLP exporter then rejected EXPORTER_SDK_AGG=dd-delta +// with `unknown ddsketch encoding: ddsketch_proto_delta`. These tests +// pin the post-#262 absorption. + +package transform + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/sdk/metric/metricdata" + mpb "go.opentelemetry.io/proto/otlp/metrics/v1" +) + +func TestDDSketchEncodingValue(t *testing.T) { + tests := []struct { + name string + in metricdata.DDSketchEncoding + want mpb.DDSketchEncoding + }{ + {"proto_full", metricdata.DDSketchEncodingProto, mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO}, + {"proto_delta", metricdata.DDSketchEncodingProtoDelta, mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO_DELTA}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := DDSketchEncodingValue(tc.in) + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestDDSketchEncodingValue_Unknown(t *testing.T) { + got, err := DDSketchEncodingValue(metricdata.DDSketchEncoding("not-a-real-encoding")) + require.Error(t, err) + assert.Equal(t, mpb.DDSketchEncoding_DDSKETCH_ENCODING_UNSPECIFIED, got) +} + +// Sibling sketches: confirm Delta + Proto map cleanly today so we +// catch regressions the next time someone touches the switch. + +func TestKLLSketchEncodingValue_Proto(t *testing.T) { + got, err := KLLSketchEncodingValue(metricdata.KLLSketchEncodingProto) + require.NoError(t, err) + assert.Equal(t, mpb.KLLSketchEncoding_KLL_SKETCH_ENCODING_PROTO, got) +} + +func TestCountSketchEncodingValue(t *testing.T) { + tests := []struct { + name string + in metricdata.CountSketchEncoding + want mpb.CountSketchEncoding + }{ + {"proto", metricdata.CountSketchEncodingProto, mpb.CountSketchEncoding_COUNT_SKETCH_ENCODING_PROTO}, + {"delta", metricdata.CountSketchEncodingDelta, mpb.CountSketchEncoding_COUNT_SKETCH_ENCODING_DELTA}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := CountSketchEncodingValue(tc.in) + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestCountMinSketchEncodingValue(t *testing.T) { + tests := []struct { + name string + in metricdata.CountMinSketchEncoding + want mpb.CountMinSketchEncoding + }{ + {"proto", metricdata.CountMinSketchEncodingProto, mpb.CountMinSketchEncoding_COUNT_MIN_SKETCH_ENCODING_PROTO}, + {"delta", metricdata.CountMinSketchEncodingDelta, mpb.CountMinSketchEncoding_COUNT_MIN_SKETCH_ENCODING_DELTA}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := CountMinSketchEncodingValue(tc.in) + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestHLLSketchEncodingValue(t *testing.T) { + tests := []struct { + name string + in metricdata.HLLSketchEncoding + want mpb.HLLSketchEncoding + }{ + {"proto", metricdata.HLLSketchEncodingProto, mpb.HLLSketchEncoding_HLL_SKETCH_ENCODING_PROTO}, + {"delta", metricdata.HLLSketchEncodingDelta, mpb.HLLSketchEncoding_HLL_SKETCH_ENCODING_DELTA}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := HLLSketchEncodingValue(tc.in) + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +} + +// TestDDSketchDataPointsCarriesProtoDeltaEncoding round-trips a +// dd-delta-encoded SDK data point through DDSketchDataPoints and +// asserts the OTLP enum lands at PROTO_DELTA. This is the path +// EXPORTER_SDK_AGG=dd-delta exercises end-to-end. +func TestDDSketchDataPointsCarriesProtoDeltaEncoding(t *testing.T) { + in := []metricdata.DDSketchDataPoint[float64]{ + { + Sketch: []byte{0x01, 0x02, 0x03}, + Encoding: metricdata.DDSketchEncodingProtoDelta, + }, + } + out, err := DDSketchDataPoints(in) + require.NoError(t, err) + require.Len(t, out, 1) + assert.Equal(t, mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO_DELTA, out[0].Encoding) + assert.Equal(t, []byte{0x01, 0x02, 0x03}, out[0].Sketch) +} diff --git a/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata.go b/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata.go index cd7c671b..ce40e388 100644 --- a/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata.go +++ b/opentelemetry-go-patch/exporters/otlp/otlpmetric/otlpmetrichttp/internal/transform/metricdata.go @@ -370,6 +370,8 @@ func DDSketchEncodingValue(enc metricdata.DDSketchEncoding) (mpb.DDSketchEncodin switch enc { case metricdata.DDSketchEncodingProto: return mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO, nil + case metricdata.DDSketchEncodingProtoDelta: + return mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO_DELTA, nil default: return mpb.DDSketchEncoding_DDSKETCH_ENCODING_UNSPECIFIED, fmt.Errorf("%w: %s", errUnknownDDSketchEncoding, enc) } diff --git a/opentelemetry-go-patch/internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl b/opentelemetry-go-patch/internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl index 057a50d2..9cb8b1b4 100644 --- a/opentelemetry-go-patch/internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl +++ b/opentelemetry-go-patch/internal/shared/otlp/otlpmetric/transform/metricdata.go.tmpl @@ -356,6 +356,8 @@ func DDSketchEncodingValue(enc metricdata.DDSketchEncoding) (mpb.DDSketchEncodin switch enc { case metricdata.DDSketchEncodingProto: return mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO, nil + case metricdata.DDSketchEncodingProtoDelta: + return mpb.DDSketchEncoding_DDSKETCH_ENCODING_PROTO_DELTA, nil default: return mpb.DDSketchEncoding_DDSKETCH_ENCODING_UNSPECIFIED, fmt.Errorf("%w: %s", errUnknownDDSketchEncoding, enc) }