Merge pull request #17284 from linasm/custom-bucket-bounds-match-fn

NHCB: Separate CustomBucketBoundsMatch from FloatBucketsMatch
This commit is contained in:
Björn Rabenstein 2025-10-07 15:38:59 +02:00 committed by GitHub
commit f2fc492473
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 97 additions and 13 deletions

View file

@ -346,7 +346,7 @@ func (h *FloatHistogram) Add(other *FloatHistogram) (res *FloatHistogram, counte
if h.UsesCustomBuckets() != other.UsesCustomBuckets() {
return nil, false, ErrHistogramsIncompatibleSchema
}
if h.UsesCustomBuckets() && !FloatBucketsMatch(h.CustomValues, other.CustomValues) {
if h.UsesCustomBuckets() && !CustomBucketBoundsMatch(h.CustomValues, other.CustomValues) {
return nil, false, ErrHistogramsIncompatibleBounds
}
@ -422,7 +422,7 @@ func (h *FloatHistogram) Sub(other *FloatHistogram) (res *FloatHistogram, counte
if h.UsesCustomBuckets() != other.UsesCustomBuckets() {
return nil, false, ErrHistogramsIncompatibleSchema
}
if h.UsesCustomBuckets() && !FloatBucketsMatch(h.CustomValues, other.CustomValues) {
if h.UsesCustomBuckets() && !CustomBucketBoundsMatch(h.CustomValues, other.CustomValues) {
return nil, false, ErrHistogramsIncompatibleBounds
}
@ -500,7 +500,7 @@ func (h *FloatHistogram) Equals(h2 *FloatHistogram) bool {
}
if h.UsesCustomBuckets() {
if !FloatBucketsMatch(h.CustomValues, h2.CustomValues) {
if !CustomBucketBoundsMatch(h.CustomValues, h2.CustomValues) {
return false
}
}
@ -513,14 +513,14 @@ func (h *FloatHistogram) Equals(h2 *FloatHistogram) bool {
if !spansMatch(h.NegativeSpans, h2.NegativeSpans) {
return false
}
if !FloatBucketsMatch(h.NegativeBuckets, h2.NegativeBuckets) {
if !floatBucketsMatch(h.NegativeBuckets, h2.NegativeBuckets) {
return false
}
if !spansMatch(h.PositiveSpans, h2.PositiveSpans) {
return false
}
if !FloatBucketsMatch(h.PositiveBuckets, h2.PositiveBuckets) {
if !floatBucketsMatch(h.PositiveBuckets, h2.PositiveBuckets) {
return false
}
@ -639,7 +639,7 @@ func (h *FloatHistogram) DetectReset(previous *FloatHistogram) bool {
if h.Count < previous.Count {
return true
}
if h.UsesCustomBuckets() != previous.UsesCustomBuckets() || (h.UsesCustomBuckets() && !FloatBucketsMatch(h.CustomValues, previous.CustomValues)) {
if h.UsesCustomBuckets() != previous.UsesCustomBuckets() || (h.UsesCustomBuckets() && !CustomBucketBoundsMatch(h.CustomValues, previous.CustomValues)) {
// Mark that something has changed or that the application has been restarted. However, this does
// not matter so much since the change in schema will be handled directly in the chunks and PromQL
// functions.
@ -1352,7 +1352,9 @@ func addBuckets(
return spansA, bucketsA
}
func FloatBucketsMatch(b1, b2 []float64) bool {
// floatBucketsMatch compares bucket values of two float histograms using binary float comparison
// and returns true if all values match.
func floatBucketsMatch(b1, b2 []float64) bool {
if len(b1) != len(b2) {
return false
}

View file

@ -80,6 +80,20 @@ func IsKnownSchema(s int32) bool {
return IsCustomBucketsSchema(s) || IsExponentialSchemaReserved(s)
}
// CustomBucketBoundsMatch compares histogram custom bucket bounds (CustomValues)
// and returns true if all values match.
func CustomBucketBoundsMatch(c1, c2 []float64) bool {
if len(c1) != len(c2) {
return false
}
for i, c := range c1 {
if c != c2[i] {
return false
}
}
return true
}
// BucketCount is a type constraint for the count in a bucket, which can be
// float64 (for type FloatHistogram) or uint64 (for type Histogram).
type BucketCount interface {

View file

@ -207,3 +207,71 @@ func TestReduceResolutionFloatHistogram(t *testing.T) {
require.Equal(t, buckets, tc.buckets[:len(buckets)])
}
}
func TestCustomBucketBoundsMatch(t *testing.T) {
tests := []struct {
name string
c1, c2 []float64
want bool
}{
{
name: "both nil",
c1: nil,
c2: nil,
want: true,
},
{
name: "both empty",
c1: []float64{},
c2: []float64{},
want: true,
},
{
name: "one empty one non-empty",
c1: []float64{},
c2: []float64{1.0},
want: false,
},
{
name: "different lengths",
c1: []float64{1.0, 2.0},
c2: []float64{1.0, 2.0, 3.0},
want: false,
},
{
name: "same single value",
c1: []float64{1.5},
c2: []float64{1.5},
want: true,
},
{
name: "different single value",
c1: []float64{1.5},
c2: []float64{2.5},
want: false,
},
{
name: "same multiple values",
c1: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
c2: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
want: true,
},
{
name: "different values",
c1: []float64{1.0, 2.1, 3.0},
c2: []float64{1.0, 2.0, 3.0},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CustomBucketBoundsMatch(tt.c1, tt.c2)
require.Equal(t, tt.want, got)
// Test commutativity (should be symmetric)
gotReverse := CustomBucketBoundsMatch(tt.c2, tt.c1)
require.Equal(t, got, gotReverse)
})
}
}

View file

@ -255,7 +255,7 @@ func (h *Histogram) Equals(h2 *Histogram) bool {
}
if h.UsesCustomBuckets() {
if !FloatBucketsMatch(h.CustomValues, h2.CustomValues) {
if !CustomBucketBoundsMatch(h.CustomValues, h2.CustomValues) {
return false
}
}

View file

@ -1139,7 +1139,7 @@ func compareNativeHistogram(exp, cur *histogram.FloatHistogram) bool {
}
if exp.UsesCustomBuckets() {
if !histogram.FloatBucketsMatch(exp.CustomValues, cur.CustomValues) {
if !histogram.CustomBucketBoundsMatch(exp.CustomValues, cur.CustomValues) {
return false
}
}

View file

@ -258,7 +258,7 @@ func (a *FloatHistogramAppender) appendable(h *histogram.FloatHistogram) (
return
}
if histogram.IsCustomBucketsSchema(h.Schema) && !histogram.FloatBucketsMatch(h.CustomValues, a.customValues) {
if histogram.IsCustomBucketsSchema(h.Schema) && !histogram.CustomBucketBoundsMatch(h.CustomValues, a.customValues) {
counterReset = true
return
}
@ -495,7 +495,7 @@ func (a *FloatHistogramAppender) appendableGauge(h *histogram.FloatHistogram) (
return
}
if histogram.IsCustomBucketsSchema(h.Schema) && !histogram.FloatBucketsMatch(h.CustomValues, a.customValues) {
if histogram.IsCustomBucketsSchema(h.Schema) && !histogram.CustomBucketBoundsMatch(h.CustomValues, a.customValues) {
return
}

View file

@ -294,7 +294,7 @@ func (a *HistogramAppender) appendable(h *histogram.Histogram) (
return
}
if histogram.IsCustomBucketsSchema(h.Schema) && !histogram.FloatBucketsMatch(h.CustomValues, a.customValues) {
if histogram.IsCustomBucketsSchema(h.Schema) && !histogram.CustomBucketBoundsMatch(h.CustomValues, a.customValues) {
counterResetHint = CounterReset
return
}
@ -532,7 +532,7 @@ func (a *HistogramAppender) appendableGauge(h *histogram.Histogram) (
return
}
if histogram.IsCustomBucketsSchema(h.Schema) && !histogram.FloatBucketsMatch(h.CustomValues, a.customValues) {
if histogram.IsCustomBucketsSchema(h.Schema) && !histogram.CustomBucketBoundsMatch(h.CustomValues, a.customValues) {
return
}