mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-09 23:24:05 -08:00
Fix handling of explicit counter reset header in histograms. (#12772)
* Fix handling of explicit counter reset header in histograms. Explicit counter reset were being ignored. Also there was no unit test coverage. Add test case for the first sample in a chunk. Add test case for non first sample in chunk. Signed-off-by: György Krajcsovits <gyorgy.krajcsovits@grafana.com> --------- Signed-off-by: György Krajcsovits <gyorgy.krajcsovits@grafana.com>
This commit is contained in:
parent
95e705612c
commit
b6f903b5f9
|
@ -233,6 +233,11 @@ func (a *FloatHistogramAppender) appendable(h *histogram.FloatHistogram) (
|
|||
if a.NumSamples() > 0 && a.GetCounterResetHeader() == GaugeType {
|
||||
return
|
||||
}
|
||||
if h.CounterResetHint == histogram.CounterReset {
|
||||
// Always honor the explicit counter reset hint.
|
||||
counterReset = true
|
||||
return
|
||||
}
|
||||
if value.IsStaleNaN(h.Sum) {
|
||||
// This is a stale sample whose buckets and spans don't matter.
|
||||
okToAppend = true
|
||||
|
@ -576,7 +581,11 @@ func (a *FloatHistogramAppender) AppendFloatHistogram(prev *FloatHistogramAppend
|
|||
return nil, false, a, nil
|
||||
}
|
||||
|
||||
if prev != nil && h.CounterResetHint != histogram.CounterReset {
|
||||
switch {
|
||||
case h.CounterResetHint == histogram.CounterReset:
|
||||
// Always honor the explicit counter reset hint.
|
||||
a.setCounterResetHeader(CounterReset)
|
||||
case prev != nil:
|
||||
// This is a new chunk, but continued from a previous one. We need to calculate the reset header unless already set.
|
||||
_, _, _, counterReset := prev.appendable(h)
|
||||
if counterReset {
|
||||
|
@ -584,9 +593,6 @@ func (a *FloatHistogramAppender) AppendFloatHistogram(prev *FloatHistogramAppend
|
|||
} else {
|
||||
a.setCounterResetHeader(NotCounterReset)
|
||||
}
|
||||
} else {
|
||||
// Honor the explicit counter reset hint.
|
||||
a.setCounterResetHeader(CounterResetHeader(h.CounterResetHint))
|
||||
}
|
||||
return nil, false, a, nil
|
||||
}
|
||||
|
|
|
@ -26,6 +26,46 @@ type floatResult struct {
|
|||
h *histogram.FloatHistogram
|
||||
}
|
||||
|
||||
func TestFirstFloatHistogramExplicitCounterReset(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
hint histogram.CounterResetHint
|
||||
expHeader CounterResetHeader
|
||||
}{
|
||||
"CounterReset": {
|
||||
hint: histogram.CounterReset,
|
||||
expHeader: CounterReset,
|
||||
},
|
||||
"NotCounterReset": {
|
||||
hint: histogram.NotCounterReset,
|
||||
expHeader: UnknownCounterReset,
|
||||
},
|
||||
"UnknownCounterReset": {
|
||||
hint: histogram.UnknownCounterReset,
|
||||
expHeader: UnknownCounterReset,
|
||||
},
|
||||
"Gauge": {
|
||||
hint: histogram.GaugeType,
|
||||
expHeader: GaugeType,
|
||||
},
|
||||
}
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
h := &histogram.FloatHistogram{
|
||||
CounterResetHint: test.hint,
|
||||
}
|
||||
chk := NewFloatHistogramChunk()
|
||||
app, err := chk.Appender()
|
||||
require.NoError(t, err)
|
||||
newChk, recoded, newApp, err := app.AppendFloatHistogram(nil, 0, h, false)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, newChk)
|
||||
require.False(t, recoded)
|
||||
require.Equal(t, app, newApp)
|
||||
require.Equal(t, test.expHeader, chk.GetCounterResetHeader())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloatHistogramChunkSameBuckets(t *testing.T) {
|
||||
c := NewFloatHistogramChunk()
|
||||
var exp []floatResult
|
||||
|
@ -399,6 +439,73 @@ func TestFloatHistogramChunkAppendable(t *testing.T) {
|
|||
|
||||
assertNewFloatHistogramChunkOnAppend(t, c, hApp, ts+1, h2, CounterReset)
|
||||
}
|
||||
|
||||
{ // New histogram that has an explicit counter reset.
|
||||
c, hApp, ts, h1 := setup()
|
||||
h2 := h1.Copy()
|
||||
h2.CounterResetHint = histogram.CounterReset
|
||||
|
||||
assertNewFloatHistogramChunkOnAppend(t, c, hApp, ts+1, h2, CounterReset)
|
||||
}
|
||||
|
||||
{ // Start new chunk explicitly, and append a new histogram that is considered appendable to the previous chunk.
|
||||
_, hApp, ts, h1 := setup()
|
||||
h2 := h1.Copy() // Identity is appendable.
|
||||
|
||||
nextChunk := NewFloatHistogramChunk()
|
||||
app, err := nextChunk.Appender()
|
||||
require.NoError(t, err)
|
||||
newChunk, recoded, newApp, err := app.AppendFloatHistogram(hApp, ts+1, h2, false)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, newChunk)
|
||||
require.False(t, recoded)
|
||||
require.Equal(t, app, newApp)
|
||||
assertSampleCount(t, nextChunk, 1, ValFloatHistogram)
|
||||
require.Equal(t, NotCounterReset, nextChunk.GetCounterResetHeader())
|
||||
}
|
||||
|
||||
{ // Start new chunk explicitly, and append a new histogram that is not considered appendable to the previous chunk.
|
||||
_, hApp, ts, h1 := setup()
|
||||
h2 := h1.Copy()
|
||||
h2.Count-- // Make this not appendable due to counter reset.
|
||||
|
||||
nextChunk := NewFloatHistogramChunk()
|
||||
app, err := nextChunk.Appender()
|
||||
require.NoError(t, err)
|
||||
newChunk, recoded, newApp, err := app.AppendFloatHistogram(hApp, ts+1, h2, false)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, newChunk)
|
||||
require.False(t, recoded)
|
||||
require.Equal(t, app, newApp)
|
||||
assertSampleCount(t, nextChunk, 1, ValFloatHistogram)
|
||||
require.Equal(t, CounterReset, nextChunk.GetCounterResetHeader())
|
||||
}
|
||||
|
||||
{ // Start new chunk explicitly, and append a new histogram that would need recoding if we added it to the chunk.
|
||||
_, hApp, ts, h1 := setup()
|
||||
h2 := h1.Copy()
|
||||
h2.PositiveSpans = []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
{Offset: 1, Length: 1},
|
||||
{Offset: 1, Length: 4},
|
||||
{Offset: 3, Length: 3},
|
||||
}
|
||||
h2.Count += 9
|
||||
h2.ZeroCount++
|
||||
h2.Sum = 30
|
||||
h2.PositiveBuckets = []float64{7, 5, 1, 3, 1, 0, 2, 5, 5, 0, 1}
|
||||
|
||||
nextChunk := NewFloatHistogramChunk()
|
||||
app, err := nextChunk.Appender()
|
||||
require.NoError(t, err)
|
||||
newChunk, recoded, newApp, err := app.AppendFloatHistogram(hApp, ts+1, h2, false)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, newChunk)
|
||||
require.False(t, recoded)
|
||||
require.Equal(t, app, newApp)
|
||||
assertSampleCount(t, nextChunk, 1, ValFloatHistogram)
|
||||
require.Equal(t, NotCounterReset, nextChunk.GetCounterResetHeader())
|
||||
}
|
||||
}
|
||||
|
||||
func assertNewFloatHistogramChunkOnAppend(t *testing.T, oldChunk Chunk, hApp *FloatHistogramAppender, ts int64, h *histogram.FloatHistogram, expectHeader CounterResetHeader) {
|
||||
|
|
|
@ -253,6 +253,11 @@ func (a *HistogramAppender) appendable(h *histogram.Histogram) (
|
|||
if a.NumSamples() > 0 && a.GetCounterResetHeader() == GaugeType {
|
||||
return
|
||||
}
|
||||
if h.CounterResetHint == histogram.CounterReset {
|
||||
// Always honor the explicit counter reset hint.
|
||||
counterReset = true
|
||||
return
|
||||
}
|
||||
if value.IsStaleNaN(h.Sum) {
|
||||
// This is a stale sample whose buckets and spans don't matter.
|
||||
okToAppend = true
|
||||
|
@ -611,7 +616,11 @@ func (a *HistogramAppender) AppendHistogram(prev *HistogramAppender, t int64, h
|
|||
return nil, false, a, nil
|
||||
}
|
||||
|
||||
if prev != nil && h.CounterResetHint != histogram.CounterReset {
|
||||
switch {
|
||||
case h.CounterResetHint == histogram.CounterReset:
|
||||
// Always honor the explicit counter reset hint.
|
||||
a.setCounterResetHeader(CounterReset)
|
||||
case prev != nil:
|
||||
// This is a new chunk, but continued from a previous one. We need to calculate the reset header unless already set.
|
||||
_, _, _, counterReset := prev.appendable(h)
|
||||
if counterReset {
|
||||
|
@ -619,9 +628,6 @@ func (a *HistogramAppender) AppendHistogram(prev *HistogramAppender, t int64, h
|
|||
} else {
|
||||
a.setCounterResetHeader(NotCounterReset)
|
||||
}
|
||||
} else {
|
||||
// Honor the explicit counter reset hint.
|
||||
a.setCounterResetHeader(CounterResetHeader(h.CounterResetHint))
|
||||
}
|
||||
return nil, false, a, nil
|
||||
}
|
||||
|
|
|
@ -27,6 +27,46 @@ type result struct {
|
|||
fh *histogram.FloatHistogram
|
||||
}
|
||||
|
||||
func TestFirstHistogramExplicitCounterReset(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
hint histogram.CounterResetHint
|
||||
expHeader CounterResetHeader
|
||||
}{
|
||||
"CounterReset": {
|
||||
hint: histogram.CounterReset,
|
||||
expHeader: CounterReset,
|
||||
},
|
||||
"NotCounterReset": {
|
||||
hint: histogram.NotCounterReset,
|
||||
expHeader: UnknownCounterReset,
|
||||
},
|
||||
"UnknownCounterReset": {
|
||||
hint: histogram.UnknownCounterReset,
|
||||
expHeader: UnknownCounterReset,
|
||||
},
|
||||
"Gauge": {
|
||||
hint: histogram.GaugeType,
|
||||
expHeader: GaugeType,
|
||||
},
|
||||
}
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
h := &histogram.Histogram{
|
||||
CounterResetHint: test.hint,
|
||||
}
|
||||
chk := NewHistogramChunk()
|
||||
app, err := chk.Appender()
|
||||
require.NoError(t, err)
|
||||
newChk, recoded, newApp, err := app.AppendHistogram(nil, 0, h, false)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, newChk)
|
||||
require.False(t, recoded)
|
||||
require.Equal(t, app, newApp)
|
||||
require.Equal(t, test.expHeader, chk.GetCounterResetHeader())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHistogramChunkSameBuckets(t *testing.T) {
|
||||
c := NewHistogramChunk()
|
||||
var exp []result
|
||||
|
@ -421,6 +461,76 @@ func TestHistogramChunkAppendable(t *testing.T) {
|
|||
|
||||
assertNewHistogramChunkOnAppend(t, c, hApp, ts+1, h2, CounterReset)
|
||||
}
|
||||
|
||||
{ // New histogram that has an explicit counter reset.
|
||||
c, hApp, ts, h1 := setup()
|
||||
h2 := h1.Copy()
|
||||
h2.CounterResetHint = histogram.CounterReset
|
||||
|
||||
assertNewHistogramChunkOnAppend(t, c, hApp, ts+1, h2, CounterReset)
|
||||
}
|
||||
|
||||
{ // Start new chunk explicitly, and append a new histogram that is considered appendable to the previous chunk.
|
||||
_, hApp, ts, h1 := setup()
|
||||
h2 := h1.Copy() // Identity is appendable.
|
||||
|
||||
nextChunk := NewHistogramChunk()
|
||||
app, err := nextChunk.Appender()
|
||||
require.NoError(t, err)
|
||||
newChunk, recoded, newApp, err := app.AppendHistogram(hApp, ts+1, h2, false)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, newChunk)
|
||||
require.False(t, recoded)
|
||||
require.Equal(t, app, newApp)
|
||||
assertSampleCount(t, nextChunk, 1, ValHistogram)
|
||||
require.Equal(t, NotCounterReset, nextChunk.GetCounterResetHeader())
|
||||
}
|
||||
|
||||
{ // Start new chunk explicitly, and append a new histogram that is not considered appendable to the previous chunk.
|
||||
_, hApp, ts, h1 := setup()
|
||||
h2 := h1.Copy()
|
||||
h2.Count-- // Make this not appendable due to counter reset.
|
||||
|
||||
nextChunk := NewHistogramChunk()
|
||||
app, err := nextChunk.Appender()
|
||||
require.NoError(t, err)
|
||||
newChunk, recoded, newApp, err := app.AppendHistogram(hApp, ts+1, h2, false)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, newChunk)
|
||||
require.False(t, recoded)
|
||||
require.Equal(t, app, newApp)
|
||||
assertSampleCount(t, nextChunk, 1, ValHistogram)
|
||||
require.Equal(t, CounterReset, nextChunk.GetCounterResetHeader())
|
||||
}
|
||||
|
||||
{ // Start new chunk explicitly, and append a new histogram that would need recoding if we added it to the chunk.
|
||||
_, hApp, ts, h1 := setup()
|
||||
h2 := h1.Copy()
|
||||
h2.PositiveSpans = []histogram.Span{
|
||||
{Offset: 0, Length: 3},
|
||||
{Offset: 1, Length: 1},
|
||||
{Offset: 1, Length: 4},
|
||||
{Offset: 3, Length: 3},
|
||||
}
|
||||
h2.Count += 9
|
||||
h2.ZeroCount++
|
||||
h2.Sum = 30
|
||||
// Existing histogram should get values converted from the above to:
|
||||
// 6 3 0 3 0 0 2 4 5 0 1 (previous values with some new empty buckets in between)
|
||||
// so the new histogram should have new counts >= these per-bucket counts, e.g.:
|
||||
h2.PositiveBuckets = []int64{7, -2, -4, 2, -2, -1, 2, 3, 0, -5, 1} // 7 5 1 3 1 0 2 5 5 0 1 (total 30)
|
||||
|
||||
nextChunk := NewHistogramChunk()
|
||||
app, err := nextChunk.Appender()
|
||||
require.NoError(t, err)
|
||||
newChunk, recoded, newApp, err := app.AppendHistogram(hApp, ts+1, h2, false)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, newChunk)
|
||||
require.False(t, recoded)
|
||||
require.Equal(t, app, newApp)
|
||||
assertSampleCount(t, nextChunk, 1, ValHistogram)
|
||||
require.Equal(t, NotCounterReset, nextChunk.GetCounterResetHeader())
|
||||
}
|
||||
}
|
||||
|
||||
func assertNewHistogramChunkOnAppend(t *testing.T, oldChunk Chunk, hApp *HistogramAppender, ts int64, h *histogram.Histogram, expectHeader CounterResetHeader) {
|
||||
|
|
Loading…
Reference in a new issue