[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] 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

View File

@ -258,10 +258,10 @@ func (ev *evaluator) combineWithInfoSeries(ctx context.Context, mat, infoMat Mat
baseSigs = append(baseSigs, sigs)
}
infoSigs := make([]string, 0, len(infoMat))
infoSigs := make(map[uint64]string, len(infoMat))
for _, s := range infoMat {
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
@ -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 []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 {
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)
}
for i, s := range info {
for _, s := range info {
if s.H != nil {
ev.error(errors.New("info sample should be float"))
}
// We encode original info sample timestamps via the float value.
origT := int64(s.F)
sig := infoSigs[i]
sig := infoSigs[s.Metric.Hash()]
if existing, exists := enh.rightSigs[sig]; exists {
// We encode original info sample timestamps via the float value.
existingOrigT := int64(existing.F)
@ -362,8 +362,8 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[i
enh.rightSigs[sig] = s
default:
// The two info samples have the same timestamp - conflict.
name := s.Metric.Map()[labels.MetricName]
ev.errorf("found duplicate series for info metric %s", name)
ev.errorf("found duplicate series for info metric: existing %s @ %d, new %s @ %d",
existing.Metric.String(), existingOrigT, s.Metric.String(), origT)
}
} else {
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.
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
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)
}