tsdb: Fix appended sample count metrics when converting float staleness markers to histograms (#17241)

tsdb: Fix appended sample count metrics when converting histogram staleness markers

Signed-off-by: Patryk Prus <p@trykpr.us>
Signed-off-by: Björn Rabenstein <github@rabenste.in>
Co-authored-by: Björn Rabenstein <github@rabenste.in>
This commit is contained in:
Patryk Prus 2025-09-30 12:49:54 -04:00 committed by GitHub
parent cc7b1de372
commit dc3e6af91a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 96 additions and 0 deletions

View File

@ -1331,6 +1331,9 @@ func (a *headAppender) commitFloats(b *appendBatch, acc *appenderCommitContext)
H: &histogram.Histogram{Sum: s.V},
})
b.histogramSeries = append(b.histogramSeries, series)
// This sample was counted as a float but is now a histogram.
acc.floatsAppended--
acc.histogramsAppended++
series.Unlock()
continue
case series.lastFloatHistogramValue != nil:
@ -1340,6 +1343,9 @@ func (a *headAppender) commitFloats(b *appendBatch, acc *appenderCommitContext)
FH: &histogram.FloatHistogram{Sum: s.V},
})
b.floatHistogramSeries = append(b.floatHistogramSeries, series)
// This sample was counted as a float but is now a float histogram.
acc.floatsAppended--
acc.histogramsAppended++
series.Unlock()
continue
}

View File

@ -34,6 +34,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/prometheus/client_golang/prometheus"
prom_testutil "github.com/prometheus/client_golang/prometheus/testutil"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/promslog"
"github.com/stretchr/testify/require"
"go.uber.org/atomic"
@ -7256,3 +7257,92 @@ func TestHead_NumStaleSeries(t *testing.T) {
appendFloatHistogram(series8, 400, staleFH)
verifySeriesCounts(4, 5)
}
// TestHistogramStalenessConversionMetrics verifies that staleness marker conversion correctly
// increments the right appender metrics for both histogram and float histogram scenarios.
func TestHistogramStalenessConversionMetrics(t *testing.T) {
testCases := []struct {
name string
setupHistogram func(app storage.Appender, lbls labels.Labels) error
}{
{
name: "float_staleness_to_histogram",
setupHistogram: func(app storage.Appender, lbls labels.Labels) error {
_, err := app.AppendHistogram(0, lbls, 1000, tsdbutil.GenerateTestHistograms(1)[0], nil)
return err
},
},
{
name: "float_staleness_to_float_histogram",
setupHistogram: func(app storage.Appender, lbls labels.Labels) error {
_, err := app.AppendHistogram(0, lbls, 1000, nil, tsdbutil.GenerateTestFloatHistograms(1)[0])
return err
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
head, _ := newTestHead(t, 1000, compression.None, false)
defer func() {
require.NoError(t, head.Close())
}()
lbls := labels.FromStrings("name", tc.name)
// Helper to get counter values
getSampleCounter := func(sampleType string) float64 {
metric := &dto.Metric{}
err := head.metrics.samplesAppended.WithLabelValues(sampleType).Write(metric)
require.NoError(t, err)
return metric.GetCounter().GetValue()
}
// Step 1: Establish a series with histogram data
app := head.Appender(context.Background())
err := tc.setupHistogram(app, lbls)
require.NoError(t, err)
require.NoError(t, app.Commit())
// Step 2: Add a float staleness marker
app = head.Appender(context.Background())
_, err = app.Append(0, lbls, 2000, math.Float64frombits(value.StaleNaN))
require.NoError(t, err)
require.NoError(t, app.Commit())
// Count what was actually stored by querying the series
q, err := NewBlockQuerier(head, 0, 3000)
require.NoError(t, err)
defer q.Close()
ss := q.Select(context.Background(), false, nil, labels.MustNewMatcher(labels.MatchEqual, "name", tc.name))
require.True(t, ss.Next())
series := ss.At()
it := series.Iterator(nil)
actualFloatSamples := 0
actualHistogramSamples := 0
for valType := it.Next(); valType != chunkenc.ValNone; valType = it.Next() {
switch valType {
case chunkenc.ValFloat:
actualFloatSamples++
case chunkenc.ValHistogram, chunkenc.ValFloatHistogram:
actualHistogramSamples++
}
}
require.NoError(t, it.Err())
// Verify what was actually stored - should be 0 floats, 2 histograms (original + converted staleness marker)
require.Equal(t, 0, actualFloatSamples, "Should have 0 float samples stored")
require.Equal(t, 2, actualHistogramSamples, "Should have 2 histogram samples: original + converted staleness marker")
// The metrics should match what was actually stored
require.Equal(t, float64(actualFloatSamples), getSampleCounter(sampleMetricTypeFloat),
"Float counter should match actual float samples stored")
require.Equal(t, float64(actualHistogramSamples), getSampleCounter(sampleMetricTypeHistogram),
"Histogram counter should match actual histogram samples stored")
})
}
}