1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-19 21:45:50 +02:00

fix: handle FixedSizeReservoir size=0 without panic (#8295)

Fixes #8232

- Added guard for size=0 in FixedSizeReservoir
- Prevents panic during Offer/update
- Added test to ensure no panic occurs

---------

Co-authored-by: David Ashpole <dashpole@google.com>
This commit is contained in:
Vansh Agarwal
2026-05-22 17:02:33 +05:30
committed by GitHub
parent 15063b23a1
commit a51ddb125a
3 changed files with 27 additions and 1 deletions
+2
View File
@@ -64,6 +64,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Fixed
- Fix `FixedSizeReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar` to safely handle zero size.
A capacity check in the constructor initializes the reservoir safely and skips initialization for zero-cap; early returns in `Offer()` and `Collect()` ensure no-op behavior. (#8295)
- Limit OTLP request size to 32 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`.
The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157)
- Limit OTLP request size to 32 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`.
+11 -1
View File
@@ -54,7 +54,9 @@ func newFixedSizeReservoir(s *storage) *FixedSizeReservoir {
r := &FixedSizeReservoir{
storage: s,
}
r.reset()
if cap(r.measurements) > 0 {
r.reset()
}
return r
}
@@ -86,6 +88,10 @@ func (*FixedSizeReservoir) randomFloat64() float64 {
// parameters are the value and dropped (filtered) attributes of the
// measurement respectively.
func (r *FixedSizeReservoir) Offer(ctx context.Context, t time.Time, n Value, a []attribute.KeyValue) {
if cap(r.measurements) == 0 {
return
}
// The following algorithm is "Algorithm L" from Li, Kim-Hung (4 December
// 1994). "Reservoir-Sampling Algorithms of Time Complexity
// O(n(1+log(N/n)))". ACM Transactions on Mathematical Software. 20 (4):
@@ -194,6 +200,10 @@ func (r *FixedSizeReservoir) advance() {
//
// The Reservoir state is preserved after this call.
func (r *FixedSizeReservoir) Collect(dest *[]Exemplar) {
if cap(r.measurements) == 0 {
*dest = (*dest)[:0]
return
}
r.mu.Lock()
defer r.mu.Unlock()
r.storage.Collect(dest)
@@ -10,6 +10,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewFixedSizeReservoir(t *testing.T) {
@@ -22,6 +23,19 @@ func TestNewFixedSizeReservoir(t *testing.T) {
}))
}
func TestNewFixedSizeReservoirZeroSize(t *testing.T) {
r := NewFixedSizeReservoir(0)
require.NotNil(t, r)
// Offer should be a no-op and not panic.
r.Offer(t.Context(), staticTime, NewValue(float64(10)), nil)
// Collect should leave dest empty.
dest := []Exemplar{{}} // pre-filled sentinel
r.Collect(&dest)
assert.Empty(t, dest)
}
func TestNewFixedSizeReservoirSamplingCorrectness(t *testing.T) {
intensity := 0.1
sampleSize := 1000