[BUGFIX] PromQL: fix more slice indexing bugs in info function (#17199)

* [BUGFIX] PromQL: fix more slice indexing bugs in info function

---------

Signed-off-by: Linas Medziunas <linas.medziunas@gmail.com>
This commit is contained in:
Linas Medžiūnas 2025-10-23 17:39:22 +02:00 committed by GitHub
parent 05612757b4
commit 20815562d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 40 additions and 13 deletions

View File

@ -55,13 +55,13 @@ func (ev *evaluator) evalInfo(ctx context.Context, args parser.Expressions) (par
}
// Don't try to enrich info series.
ignoreSeries := map[int]struct{}{}
ignoreSeries := map[uint64]struct{}{}
loop:
for i, s := range mat {
for _, s := range mat {
name := s.Metric.Get(labels.MetricName)
for _, m := range infoNameMatchers {
if m.Matches(name) {
ignoreSeries[i] = struct{}{}
ignoreSeries[s.Metric.Hash()] = struct{}{}
continue loop
}
}
@ -121,11 +121,11 @@ func (ev *evaluator) infoSelectHints(expr parser.Expr) storage.SelectHints {
// fetchInfoSeries fetches info series given matching identifying labels in mat.
// Series in ignoreSeries are not fetched.
// dataLabelMatchers may be mutated.
func (ev *evaluator) fetchInfoSeries(ctx context.Context, mat Matrix, ignoreSeries map[int]struct{}, dataLabelMatchers map[string][]*labels.Matcher, selectHints storage.SelectHints) (Matrix, annotations.Annotations, error) {
func (ev *evaluator) fetchInfoSeries(ctx context.Context, mat Matrix, ignoreSeries map[uint64]struct{}, dataLabelMatchers map[string][]*labels.Matcher, selectHints storage.SelectHints) (Matrix, annotations.Annotations, error) {
// A map of values for all identifying labels we are interested in.
idLblValues := map[string]map[string]struct{}{}
for i, s := range mat {
if _, exists := ignoreSeries[i]; exists {
for _, s := range mat {
if _, exists := ignoreSeries[s.Metric.Hash()]; exists {
continue
}
@ -197,7 +197,7 @@ func (ev *evaluator) fetchInfoSeries(ctx context.Context, mat Matrix, ignoreSeri
}
// combineWithInfoSeries combines mat with select data labels from infoMat.
func (ev *evaluator) combineWithInfoSeries(ctx context.Context, mat, infoMat Matrix, ignoreSeries map[int]struct{}, dataLabelMatchers map[string][]*labels.Matcher) (Matrix, annotations.Annotations) {
func (ev *evaluator) combineWithInfoSeries(ctx context.Context, mat, infoMat Matrix, ignoreSeries map[uint64]struct{}, dataLabelMatchers map[string][]*labels.Matcher) (Matrix, annotations.Annotations) {
buf := make([]byte, 0, 1024)
lb := labels.NewScratchBuilder(0)
sigFunction := func(name string) func(labels.Labels) string {
@ -249,13 +249,13 @@ func (ev *evaluator) combineWithInfoSeries(ctx context.Context, mat, infoMat Mat
tempNumSamples := ev.currentSamples
// For every base series, compute signature per info metric.
baseSigs := make([]map[string]string, 0, len(mat))
baseSigs := make(map[uint64]map[string]string, len(mat))
for _, s := range mat {
sigs := make(map[string]string, len(infoMetrics))
for infoName := range infoMetrics {
sigs[infoName] = sigfs[infoName](s.Metric)
}
baseSigs = append(baseSigs, sigs)
baseSigs[s.Metric.Hash()] = sigs
}
infoSigs := make(map[uint64]string, len(infoMat))
@ -331,7 +331,7 @@ func (ev *evaluator) combineWithInfoSeries(ctx context.Context, mat, infoMat Mat
// combineWithInfoVector combines base and info Vectors.
// Base series in ignoreSeries are not combined.
func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[int]struct{}, baseSigs []map[string]string, infoSigs map[uint64]string, enh *EvalNodeHelper, dataLabelMatchers map[string][]*labels.Matcher) (Vector, error) {
func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[uint64]struct{}, baseSigs map[uint64]map[string]string, infoSigs map[uint64]string, enh *EvalNodeHelper, dataLabelMatchers map[string][]*labels.Matcher) (Vector, error) {
if len(base) == 0 {
return nil, nil // Short-circuit: nothing is going to match.
}
@ -370,8 +370,10 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[i
}
}
for i, bs := range base {
if _, exists := ignoreSeries[i]; exists {
for _, bs := range base {
hash := bs.Metric.Hash()
if _, exists := ignoreSeries[hash]; exists {
// This series should not be enriched with info metric data labels.
enh.Out = append(enh.Out, Sample{
Metric: bs.Metric,
@ -386,7 +388,7 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[i
// For every info metric name, try to find an info series with the same signature.
seenInfoMetrics := map[string]struct{}{}
for infoName, sig := range baseSigs[i] {
for infoName, sig := range baseSigs[hash] {
is, exists := enh.rightSigs[sig]
if !exists {
continue

View File

@ -114,6 +114,8 @@ eval range from 1m to 4m step 1m info(metric offset 1m)
clear
# info_metric churn:
load 1m
data_metric{instance="a", job="work"} 10 20 30
data_metric{instance="b", job="work"} 11 21 31
@ -125,3 +127,26 @@ eval range from 0 to 2m step 1m info(data_metric, {__name__="info_metric"})
data_metric{instance="a", job="work", state="running"} 10 20 30
data_metric{instance="b", job="work", state="stopped"} 11 21 _
data_metric{instance="b", job="work", state="running"} _ _ 31
clear
# data_metric churn:
load 1m
data_metric{instance="a", job="work"} 10 20 stale
data_metric{instance="b", job="work"} 11 21 31
data_metric{instance="a", job="work", label="new"} _ _ 30
info_metric{instance="b", job="work", state="stopped"} 1 1 1
info_metric{instance="a", job="work", state="running"} 1 1 1
eval range from 0 to 2m step 1m info(data_metric, {__name__="info_metric"})
data_metric{instance="a", job="work", state="running"} 10 20 _
data_metric{instance="b", job="work", state="stopped"} 11 21 31
data_metric{instance="a", job="work", state="running", label="new"} _ _ 30
eval range from 0 to 2m step 1m info({job="work"}, {__name__="info_metric"})
data_metric{instance="a", job="work", state="running"} 10 20 _
data_metric{instance="b", job="work", state="stopped"} 11 21 31
data_metric{instance="a", job="work", state="running", label="new"} _ _ 30
info_metric{instance="b", job="work", state="stopped"} 1 1 1
info_metric{instance="a", job="work", state="running"} 1 1 1