[BUGFIX] PromQL: fix slice indexing bug in info function (#17135)

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

---------

Signed-off-by: Linas Medziunas <linas.medziunas@gmail.com>
Signed-off-by: Linas Medžiūnas <linasm@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Linas Medžiūnas 2025-09-05 15:46:57 +03:00 committed by GitHub
parent 43c1535bdf
commit 5c2e43f09c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 23 additions and 7 deletions

View File

@ -4,6 +4,7 @@
* [BUGFIX] OTLP receiver: Generate `target_info` samples between the earliest and latest samples per resource. #16737 * [BUGFIX] OTLP receiver: Generate `target_info` samples between the earliest and latest samples per resource. #16737
* [BUGFIX] Config: Infer escaping scheme when scrape config validation scheme is set. * [BUGFIX] Config: Infer escaping scheme when scrape config validation scheme is set.
* [BUGFIX] PromQL: Fix info function on churning series. #17135
## 3.5.0 / 2025-07-14 ## 3.5.0 / 2025-07-14

View File

@ -258,10 +258,10 @@ func (ev *evaluator) combineWithInfoSeries(ctx context.Context, mat, infoMat Mat
baseSigs = append(baseSigs, sigs) baseSigs = append(baseSigs, sigs)
} }
infoSigs := make([]string, 0, len(infoMat)) infoSigs := make(map[uint64]string, len(infoMat))
for _, s := range infoMat { for _, s := range infoMat {
name := s.Metric.Map()[labels.MetricName] name := s.Metric.Map()[labels.MetricName]
infoSigs = append(infoSigs, sigfs[name](s.Metric)) infoSigs[s.Metric.Hash()] = sigfs[name](s.Metric)
} }
var warnings annotations.Annotations var warnings annotations.Annotations
@ -331,7 +331,7 @@ func (ev *evaluator) combineWithInfoSeries(ctx context.Context, mat, infoMat Mat
// combineWithInfoVector combines base and info Vectors. // combineWithInfoVector combines base and info Vectors.
// Base series in ignoreSeries are not combined. // Base series in ignoreSeries are not combined.
func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[int]struct{}, baseSigs []map[string]string, infoSigs []string, enh *EvalNodeHelper, dataLabelMatchers map[string][]*labels.Matcher) (Vector, error) { 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) {
if len(base) == 0 { if len(base) == 0 {
return nil, nil // Short-circuit: nothing is going to match. return nil, nil // Short-circuit: nothing is going to match.
} }
@ -343,14 +343,14 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[i
clear(enh.rightSigs) clear(enh.rightSigs)
} }
for i, s := range info { for _, s := range info {
if s.H != nil { if s.H != nil {
ev.error(errors.New("info sample should be float")) ev.error(errors.New("info sample should be float"))
} }
// We encode original info sample timestamps via the float value. // We encode original info sample timestamps via the float value.
origT := int64(s.F) origT := int64(s.F)
sig := infoSigs[i] sig := infoSigs[s.Metric.Hash()]
if existing, exists := enh.rightSigs[sig]; exists { if existing, exists := enh.rightSigs[sig]; exists {
// We encode original info sample timestamps via the float value. // We encode original info sample timestamps via the float value.
existingOrigT := int64(existing.F) existingOrigT := int64(existing.F)
@ -362,8 +362,8 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[i
enh.rightSigs[sig] = s enh.rightSigs[sig] = s
default: default:
// The two info samples have the same timestamp - conflict. // The two info samples have the same timestamp - conflict.
name := s.Metric.Map()[labels.MetricName] ev.errorf("found duplicate series for info metric: existing %s @ %d, new %s @ %d",
ev.errorf("found duplicate series for info metric %s", name) existing.Metric.String(), existingOrigT, s.Metric.String(), origT)
} }
} else { } else {
enh.rightSigs[sig] = s enh.rightSigs[sig] = s

View File

@ -136,5 +136,20 @@ eval range from 1m to 4m step 1m info(metric @ 60)
# offset operator works also with info. # offset operator works also with info.
eval range from 1m to 4m step 1m info(metric offset 1m) eval range from 1m to 4m step 1m info(metric offset 1m)
metric{data="info", instance="a", job="1", label="value"} 0 0 2 3 metric{data="info", instance="a", job="1", label="value"} 0 0 2 3
clear
load 1m
data_metric{instance="a", job="work"} 10 20 30
data_metric{instance="b", job="work"} 11 21 31
info_metric{instance="b", job="work", state="stopped"} 1 1 _
info_metric{instance="b", job="work", state="running"} _ _ 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 30
data_metric{instance="b", job="work", state="stopped"} 11 21 _
data_metric{instance="b", job="work", state="running"} _ _ 31
`, engine) `, engine)
} }