Fix labels handling with dedupelabels tag

Use scratch builder.
Use hash compare instead of compare by label.

Signed-off-by: György Krajcsovits <gyorgy.krajcsovits@grafana.com>
This commit is contained in:
György Krajcsovits 2024-10-09 12:29:59 +02:00
parent 6b15791ec6
commit 8dfa733596
3 changed files with 29 additions and 37 deletions

View file

@ -28,14 +28,15 @@ import (
"github.com/prometheus/prometheus/util/convertnhcb" "github.com/prometheus/prometheus/util/convertnhcb"
) )
var errLabelsMismatch = errors.New("labels mismatch")
type NHCBParser struct { type NHCBParser struct {
// The parser we're wrapping. // The parser we're wrapping.
parser Parser parser Parser
// Option to keep classic histograms along with converted histograms. // Option to keep classic histograms along with converted histograms.
keepClassicHistograms bool keepClassicHistograms bool
// Labels builder.
builder labels.ScratchBuilder
// Caches the values from the underlying parser. // Caches the values from the underlying parser.
// For Series and Histogram. // For Series and Histogram.
bytes []byte bytes []byte
@ -77,10 +78,11 @@ type NHCBParser struct {
lastBaseHistLabels labels.Labels lastBaseHistLabels labels.Labels
} }
func NewNHCBParser(p Parser, keepClassicHistograms bool) Parser { func NewNHCBParser(p Parser, st *labels.SymbolTable, keepClassicHistograms bool) Parser {
return &NHCBParser{ return &NHCBParser{
parser: p, parser: p,
keepClassicHistograms: keepClassicHistograms, keepClassicHistograms: keepClassicHistograms,
builder: labels.NewScratchBuilderWithSymbolTable(st, 16),
tempNHCB: convertnhcb.NewTempHistogram(), tempNHCB: convertnhcb.NewTempHistogram(),
} }
} }
@ -198,48 +200,37 @@ func (p *NHCBParser) compareLabels() bool {
return true return true
} }
// Compare the labels. if p.lastBaseHistLabels.Get(labels.MetricName) != convertnhcb.GetHistogramMetricBaseName(p.lset.Get(labels.MetricName)) {
err := p.lset.Validate(func(l labels.Label) error {
switch {
case l.Name == labels.BucketLabel:
case l.Name == labels.MetricName:
baseName := convertnhcb.GetHistogramMetricBaseName(l.Value)
if baseName != p.lastBaseHistLabels.Get(labels.MetricName) {
// Different metric name. // Different metric name.
if p.typ == model.MetricTypeHistogram {
p.storeBaseLabels() p.storeBaseLabels()
} else { return true
p.lastBaseHistLabels = labels.EmptyLabels()
} }
return errLabelsMismatch var buf []byte
} lastHash, _ := p.lastBaseHistLabels.HashWithoutLabels(buf, labels.BucketLabel)
case l.Value != p.lastBaseHistLabels.Get(l.Name): nextHash, _ := p.lset.HashWithoutLabels(buf, labels.BucketLabel)
// Different label value. if lastHash != nextHash {
if p.typ == model.MetricTypeHistogram { // Different label values.
p.storeBaseLabels() p.storeBaseLabels()
} else { return true
p.lastBaseHistLabels = labels.EmptyLabels()
} }
return errLabelsMismatch
} return false
return nil
})
return errors.Is(err, errLabelsMismatch)
} }
// Save the label set of the classic histogram without suffix and bucket `le` label. // Save the label set of the classic histogram without suffix and bucket `le` label.
func (p *NHCBParser) storeBaseLabels() { func (p *NHCBParser) storeBaseLabels() {
builder := labels.Builder{} p.builder.Reset()
p.lset.Range(func(l labels.Label) { p.lset.Range(func(l labels.Label) {
switch { switch {
case l.Name == labels.BucketLabel: case l.Name == labels.BucketLabel:
case l.Name == labels.MetricName: case l.Name == labels.MetricName:
builder.Set(l.Name, convertnhcb.GetHistogramMetricBaseName(l.Value)) p.builder.Add(l.Name, convertnhcb.GetHistogramMetricBaseName(l.Value))
default: default:
builder.Set(l.Name, l.Value) p.builder.Add(l.Name, l.Value)
} }
}) })
p.lastBaseHistLabels = builder.Labels() // Note: we don't sort the labels as changing the name label value doesn't affect sorting.
p.lastBaseHistLabels = p.builder.Labels()
} }
// handleClassicHistogramSeries collates the classic histogram series to be converted to NHCB // handleClassicHistogramSeries collates the classic histogram series to be converted to NHCB

View file

@ -441,7 +441,7 @@ foobar{quantile="0.99"} 150.1`
} }
p := NewOpenMetricsParser([]byte(input), labels.NewSymbolTable(), WithOMParserCTSeriesSkipped()) p := NewOpenMetricsParser([]byte(input), labels.NewSymbolTable(), WithOMParserCTSeriesSkipped())
p = NewNHCBParser(p, false) p = NewNHCBParser(p, labels.NewSymbolTable(), false)
got := testParse(t, p) got := testParse(t, p)
requireEntries(t, exp, got) requireEntries(t, exp, got)
} }
@ -508,7 +508,8 @@ something_bucket{a="b",le="+Inf"} 9 # {id="something-test"} 2e100 123.000
} }
p := NewOpenMetricsParser([]byte(input), labels.NewSymbolTable(), WithOMParserCTSeriesSkipped()) p := NewOpenMetricsParser([]byte(input), labels.NewSymbolTable(), WithOMParserCTSeriesSkipped())
p = NewNHCBParser(p, false)
p = NewNHCBParser(p, labels.NewSymbolTable(), false)
got := testParse(t, p) got := testParse(t, p)
requireEntries(t, exp, got) requireEntries(t, exp, got)
} }

View file

@ -1546,7 +1546,7 @@ type appendErrors struct {
func (sl *scrapeLoop) append(app storage.Appender, b []byte, contentType string, ts time.Time) (total, added, seriesAdded int, err error) { func (sl *scrapeLoop) append(app storage.Appender, b []byte, contentType string, ts time.Time) (total, added, seriesAdded int, err error) {
p, err := textparse.New(b, contentType, sl.scrapeClassicHistograms, sl.enableCTZeroIngestion, sl.symbolTable) p, err := textparse.New(b, contentType, sl.scrapeClassicHistograms, sl.enableCTZeroIngestion, sl.symbolTable)
if sl.convertClassicHistograms { if sl.convertClassicHistograms {
p = textparse.NewNHCBParser(p, sl.scrapeClassicHistograms) p = textparse.NewNHCBParser(p, sl.symbolTable, sl.scrapeClassicHistograms)
} }
if err != nil { if err != nil {
sl.l.Debug( sl.l.Debug(