This commit is contained in:
Julius Hinze 2025-08-05 17:40:33 +02:00 committed by GitHub
commit 5bf41fad55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 152 additions and 101 deletions

View File

@ -294,6 +294,9 @@ func (h *FloatHistogram) Mul(factor float64) *FloatHistogram {
for i := range h.NegativeBuckets {
h.NegativeBuckets[i] *= factor
}
if factor < 0 {
h.CounterResetHint = GaugeType
}
return h
}
@ -317,6 +320,9 @@ func (h *FloatHistogram) Div(scalar float64) *FloatHistogram {
for i := range h.NegativeBuckets {
h.NegativeBuckets[i] /= scalar
}
if scalar < 0 {
h.CounterResetHint = GaugeType
}
return h
}
@ -410,53 +416,7 @@ func (h *FloatHistogram) Add(other *FloatHistogram) (*FloatHistogram, error) {
// Sub works like Add but subtracts the other histogram.
func (h *FloatHistogram) Sub(other *FloatHistogram) (*FloatHistogram, error) {
if h.UsesCustomBuckets() != other.UsesCustomBuckets() {
return nil, ErrHistogramsIncompatibleSchema
}
if h.UsesCustomBuckets() && !FloatBucketsMatch(h.CustomValues, other.CustomValues) {
return nil, ErrHistogramsIncompatibleBounds
}
if !h.UsesCustomBuckets() {
otherZeroCount := h.reconcileZeroBuckets(other)
h.ZeroCount -= otherZeroCount
}
h.Count -= other.Count
h.Sum -= other.Sum
var (
hPositiveSpans = h.PositiveSpans
hPositiveBuckets = h.PositiveBuckets
otherPositiveSpans = other.PositiveSpans
otherPositiveBuckets = other.PositiveBuckets
)
if h.UsesCustomBuckets() {
h.PositiveSpans, h.PositiveBuckets = addBuckets(h.Schema, h.ZeroThreshold, true, hPositiveSpans, hPositiveBuckets, otherPositiveSpans, otherPositiveBuckets)
return h, nil
}
var (
hNegativeSpans = h.NegativeSpans
hNegativeBuckets = h.NegativeBuckets
otherNegativeSpans = other.NegativeSpans
otherNegativeBuckets = other.NegativeBuckets
)
switch {
case other.Schema < h.Schema:
hPositiveSpans, hPositiveBuckets = reduceResolution(hPositiveSpans, hPositiveBuckets, h.Schema, other.Schema, false, true)
hNegativeSpans, hNegativeBuckets = reduceResolution(hNegativeSpans, hNegativeBuckets, h.Schema, other.Schema, false, true)
h.Schema = other.Schema
case other.Schema > h.Schema:
otherPositiveSpans, otherPositiveBuckets = reduceResolution(otherPositiveSpans, otherPositiveBuckets, other.Schema, h.Schema, false, false)
otherNegativeSpans, otherNegativeBuckets = reduceResolution(otherNegativeSpans, otherNegativeBuckets, other.Schema, h.Schema, false, false)
}
h.PositiveSpans, h.PositiveBuckets = addBuckets(h.Schema, h.ZeroThreshold, true, hPositiveSpans, hPositiveBuckets, otherPositiveSpans, otherPositiveBuckets)
h.NegativeSpans, h.NegativeBuckets = addBuckets(h.Schema, h.ZeroThreshold, true, hNegativeSpans, hNegativeBuckets, otherNegativeSpans, otherNegativeBuckets)
return h, nil
return h.Add(other.Copy().Mul(-1))
}
// Equals returns true if the given float histogram matches exactly.

View File

@ -145,14 +145,15 @@ func TestFloatHistogramMul(t *testing.T) {
},
-1,
&FloatHistogram{
ZeroThreshold: 0.01,
ZeroCount: -11,
Count: -30,
Sum: -23,
PositiveSpans: []Span{{-2, 2}, {1, 3}},
PositiveBuckets: []float64{-1, 0, -3, -4, -7},
NegativeSpans: []Span{{3, 2}, {3, 2}},
NegativeBuckets: []float64{-3, -1, -5, -6},
ZeroThreshold: 0.01,
ZeroCount: -11,
Count: -30,
Sum: -23,
PositiveSpans: []Span{{-2, 2}, {1, 3}},
PositiveBuckets: []float64{-1, 0, -3, -4, -7},
NegativeSpans: []Span{{3, 2}, {3, 2}},
NegativeBuckets: []float64{-3, -1, -5, -6},
CounterResetHint: GaugeType,
},
},
{
@ -169,14 +170,15 @@ func TestFloatHistogramMul(t *testing.T) {
},
-2,
&FloatHistogram{
ZeroThreshold: 0.01,
ZeroCount: -22,
Count: -60,
Sum: -46,
PositiveSpans: []Span{{-2, 2}, {1, 3}},
PositiveBuckets: []float64{-2, 0, -6, -8, -14},
NegativeSpans: []Span{{3, 2}, {3, 2}},
NegativeBuckets: []float64{-6, -2, -10, -12},
ZeroThreshold: 0.01,
ZeroCount: -22,
Count: -60,
Sum: -46,
PositiveSpans: []Span{{-2, 2}, {1, 3}},
PositiveBuckets: []float64{-2, 0, -6, -8, -14},
NegativeSpans: []Span{{3, 2}, {3, 2}},
NegativeBuckets: []float64{-6, -2, -10, -12},
CounterResetHint: GaugeType,
},
},
{
@ -467,14 +469,15 @@ func TestFloatHistogramDiv(t *testing.T) {
},
-1,
&FloatHistogram{
ZeroThreshold: 0.01,
ZeroCount: -5.5,
Count: -3493.3,
Sum: -2349209.324,
PositiveSpans: []Span{{-2, 1}, {2, 3}},
PositiveBuckets: []float64{-1, -3.3, -4.2, -0.1},
NegativeSpans: []Span{{3, 2}, {3, 2}},
NegativeBuckets: []float64{-3.1, -3, -1.234e5, -1000},
ZeroThreshold: 0.01,
ZeroCount: -5.5,
Count: -3493.3,
Sum: -2349209.324,
PositiveSpans: []Span{{-2, 1}, {2, 3}},
PositiveBuckets: []float64{-1, -3.3, -4.2, -0.1},
NegativeSpans: []Span{{3, 2}, {3, 2}},
NegativeBuckets: []float64{-3.1, -3, -1.234e5, -1000},
CounterResetHint: GaugeType,
},
},
{
@ -491,14 +494,15 @@ func TestFloatHistogramDiv(t *testing.T) {
},
-2,
&FloatHistogram{
ZeroThreshold: 0.01,
ZeroCount: -5.5,
Count: -15,
Sum: -11.5,
PositiveSpans: []Span{{-2, 2}, {1, 3}},
PositiveBuckets: []float64{-0.5, 0, -1.5, -2, -3.5},
NegativeSpans: []Span{{3, 2}, {3, 2}},
NegativeBuckets: []float64{-1.5, -0.5, -2.5, -3},
ZeroThreshold: 0.01,
ZeroCount: -5.5,
Count: -15,
Sum: -11.5,
PositiveSpans: []Span{{-2, 2}, {1, 3}},
PositiveBuckets: []float64{-0.5, 0, -1.5, -2, -3.5},
NegativeSpans: []Span{{3, 2}, {3, 2}},
NegativeBuckets: []float64{-1.5, -0.5, -2.5, -3},
CounterResetHint: GaugeType,
},
},
{
@ -2379,14 +2383,15 @@ func TestFloatHistogramSub(t *testing.T) {
NegativeBuckets: []float64{1, 1, 4, 4},
},
&FloatHistogram{
ZeroThreshold: 0.01,
ZeroCount: 3,
Count: 9,
Sum: 11,
PositiveSpans: []Span{{-2, 2}, {1, 3}},
PositiveBuckets: []float64{1, 0, 1, 1, 1},
NegativeSpans: []Span{{3, 2}, {3, 2}},
NegativeBuckets: []float64{2, 0, 1, 2},
ZeroThreshold: 0.01,
ZeroCount: 3,
Count: 9,
Sum: 11,
PositiveSpans: []Span{{-2, 2}, {1, 3}},
PositiveBuckets: []float64{1, 0, 1, 1, 1},
NegativeSpans: []Span{{3, 2}, {3, 2}},
NegativeBuckets: []float64{2, 0, 1, 2},
CounterResetHint: GaugeType,
},
"",
},
@ -2415,14 +2420,15 @@ func TestFloatHistogramSub(t *testing.T) {
NegativeBuckets: []float64{3, 0.5, 0.5, 2, 3, 2, 4},
},
&FloatHistogram{
ZeroThreshold: 0.01,
ZeroCount: 6,
Count: 40,
Sum: 0.889,
PositiveSpans: []Span{{-2, 5}, {0, 3}},
PositiveBuckets: []float64{1, 5, 4, 2, 2, 2, 0, 5},
NegativeSpans: []Span{{3, 3}, {1, 3}},
NegativeBuckets: []float64{1, 9, 1, 4, 9, 1},
ZeroThreshold: 0.01,
ZeroCount: 6,
Count: 40,
Sum: 0.889,
PositiveSpans: []Span{{-2, 5}, {0, 3}},
PositiveBuckets: []float64{1, 5, 4, 2, 2, 2, 0, 5},
NegativeSpans: []Span{{3, 3}, {1, 3}},
NegativeBuckets: []float64{1, 9, 1, 4, 9, 1},
CounterResetHint: GaugeType,
},
"",
},
@ -2445,12 +2451,13 @@ func TestFloatHistogramSub(t *testing.T) {
CustomValues: []float64{1, 2, 3, 4},
},
&FloatHistogram{
Schema: CustomBucketsSchema,
Count: 4,
Sum: 11,
PositiveSpans: []Span{{0, 2}, {1, 3}},
PositiveBuckets: []float64{1, 0, 1, 1, 1},
CustomValues: []float64{1, 2, 3, 4},
Schema: CustomBucketsSchema,
Count: 4,
Sum: 11,
PositiveSpans: []Span{{0, 2}, {1, 3}},
PositiveBuckets: []float64{1, 0, 1, 1, 1},
CustomValues: []float64{1, 2, 3, 4},
CounterResetHint: GaugeType,
},
"",
},

View File

@ -4049,3 +4049,85 @@ func TestSubQueryHistogramsCopy(t *testing.T) {
queryable.Close()
}
}
func TestHistogram_CounterResetHint(t *testing.T) {
baseT := timestamp.Time(0)
load := `
load 2m
test_histogram {{count:0}}+{{count:1}}x4
`
testCases := []struct {
name string
query string
want histogram.CounterResetHint
}{
{
name: "adding histograms",
query: `test_histogram + test_histogram`,
want: histogram.NotCounterReset,
},
{
name: "subtraction",
query: `test_histogram - test_histogram`,
want: histogram.GaugeType,
},
{
name: "negated addition",
query: `test_histogram + (-test_histogram)`,
want: histogram.GaugeType,
},
{
name: "negated subtraction",
query: `test_histogram - (-test_histogram)`,
want: histogram.GaugeType,
},
{
name: "unary negation",
query: `-test_histogram`,
want: histogram.GaugeType,
},
{
name: "repeated unary negation",
query: `-(-test_histogram)`,
want: histogram.GaugeType,
},
{
name: "multiplication",
query: `2 * test_histogram`,
want: histogram.NotCounterReset,
},
{
name: "negative multiplication",
query: `-1 * test_histogram`,
want: histogram.GaugeType,
},
{
name: "division",
query: `test_histogram / 2`,
want: histogram.NotCounterReset,
},
{
name: "negative division",
query: `test_histogram / -1`,
want: histogram.GaugeType,
},
}
ctx := context.Background()
queryable := promqltest.LoadedStorage(t, load)
defer queryable.Close()
engine := promqltest.NewTestEngine(t, false, 0, promqltest.DefaultMaxSamplesPerQuery)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
q, err := engine.NewInstantQuery(ctx, queryable, nil, tc.query, baseT.Add(2*time.Minute))
require.NoError(t, err)
defer q.Close()
res := q.Exec(ctx)
require.NoError(t, res.Err)
v, err := res.Vector()
require.NoError(t, err)
require.Len(t, v, 1)
require.NotNil(t, v[0].H)
require.Equal(t, tc.want, v[0].H.CounterResetHint)
})
}
}

View File

@ -5310,6 +5310,7 @@ func TestParseHistogramSeries(t *testing.T) {
Offset: 0,
Length: 3,
}},
CounterResetHint: histogram.GaugeType,
},
{
Schema: 1,
@ -5318,6 +5319,7 @@ func TestParseHistogramSeries(t *testing.T) {
Offset: 0,
Length: 3,
}},
CounterResetHint: histogram.GaugeType,
},
},
},