mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-26 06:04:05 -08:00
Add duplicate-labelset check for range/instant vectors (#4589)
Signed-off-by: Harsh Agarwal <cs15btech11019@iith.ac.in>
This commit is contained in:
parent
cfbd72b4f1
commit
18a9a390b5
|
@ -720,6 +720,9 @@ func (ev *evaluator) rangeEval(f func([]Value, *EvalNodeHelper) Vector, exprs ..
|
|||
// Make the function call.
|
||||
enh.ts = ts
|
||||
result := f(args, enh)
|
||||
if result.ContainsSameLabelset() {
|
||||
ev.errorf("vector cannot contain metrics with the same labelset")
|
||||
}
|
||||
enh.out = result[:0] // Reuse result vector.
|
||||
// If this could be an instant query, shortcut so as not to change sort order.
|
||||
if ev.endTimestamp == ev.startTimestamp {
|
||||
|
@ -883,6 +886,10 @@ func (ev *evaluator) eval(expr Expr) Value {
|
|||
mat = append(mat, ss)
|
||||
}
|
||||
}
|
||||
if mat.ContainsSameLabelset() {
|
||||
ev.errorf("vector cannot contain metrics with the same labelset")
|
||||
}
|
||||
|
||||
putPointSlice(points)
|
||||
return mat
|
||||
|
||||
|
@ -898,6 +905,9 @@ func (ev *evaluator) eval(expr Expr) Value {
|
|||
mat[i].Points[j].V = -mat[i].Points[j].V
|
||||
}
|
||||
}
|
||||
if mat.ContainsSameLabelset() {
|
||||
ev.errorf("vector cannot contain metrics with the same labelset")
|
||||
}
|
||||
}
|
||||
return mat
|
||||
|
||||
|
|
|
@ -734,7 +734,6 @@ func funcLabelReplace(vals []Value, args Expressions, enh *EvalNodeHelper) Vecto
|
|||
enh.dmn = make(map[uint64]labels.Labels, len(enh.out))
|
||||
}
|
||||
|
||||
outSet := make(map[uint64]struct{}, len(vector))
|
||||
for _, el := range vector {
|
||||
h := el.Metric.Hash()
|
||||
var outMetric labels.Labels
|
||||
|
@ -759,17 +758,10 @@ func funcLabelReplace(vals []Value, args Expressions, enh *EvalNodeHelper) Vecto
|
|||
}
|
||||
}
|
||||
|
||||
outHash := outMetric.Hash()
|
||||
if _, ok := outSet[outHash]; ok {
|
||||
panic(fmt.Errorf("duplicated label set in output of label_replace(): %s", el.Metric))
|
||||
} else {
|
||||
enh.out = append(enh.out,
|
||||
Sample{
|
||||
enh.out = append(enh.out, Sample{
|
||||
Metric: outMetric,
|
||||
Point: Point{V: el.Point.V},
|
||||
})
|
||||
outSet[outHash] = struct{}{}
|
||||
}
|
||||
}
|
||||
return enh.out
|
||||
}
|
||||
|
@ -808,7 +800,6 @@ func funcLabelJoin(vals []Value, args Expressions, enh *EvalNodeHelper) Vector {
|
|||
panic(fmt.Errorf("invalid destination label name in label_join(): %s", dst))
|
||||
}
|
||||
|
||||
outSet := make(map[uint64]struct{}, len(vector))
|
||||
srcVals := make([]string, len(srcLabels))
|
||||
for _, el := range vector {
|
||||
h := el.Metric.Hash()
|
||||
|
@ -833,17 +824,11 @@ func funcLabelJoin(vals []Value, args Expressions, enh *EvalNodeHelper) Vector {
|
|||
outMetric = lb.Labels()
|
||||
enh.dmn[h] = outMetric
|
||||
}
|
||||
outHash := outMetric.Hash()
|
||||
|
||||
if _, exists := outSet[outHash]; exists {
|
||||
panic(fmt.Errorf("duplicated label set in output of label_join(): %s", el.Metric))
|
||||
} else {
|
||||
enh.out = append(enh.out, Sample{
|
||||
Metric: outMetric,
|
||||
Point: Point{V: el.Point.V},
|
||||
})
|
||||
outSet[outHash] = struct{}{}
|
||||
}
|
||||
}
|
||||
return enh.out
|
||||
}
|
||||
|
|
8
promql/testdata/functions.test
vendored
8
promql/testdata/functions.test
vendored
|
@ -514,3 +514,11 @@ eval instant at 0m days_in_month(vector(1454284800))
|
|||
eval instant at 0m days_in_month(vector(1485907200))
|
||||
{} 28
|
||||
|
||||
clear
|
||||
|
||||
# Test duplicate labelset in promql output.
|
||||
load 5m
|
||||
testmetric1{src="a",dst="b"} 0
|
||||
testmetric2{src="a",dst="b"} 1
|
||||
|
||||
eval_fail instant at 0m changes({__name__=~'testmetric1|testmetric2'}[5m])
|
9
promql/testdata/legacy.test
vendored
9
promql/testdata/legacy.test
vendored
|
@ -357,3 +357,12 @@ load 1h
|
|||
eval instant at 0h testmetric
|
||||
testmetric{aa="bb"} 1
|
||||
testmetric{a="abb"} 2
|
||||
|
||||
clear
|
||||
|
||||
# Test duplicate labelset in promql output.
|
||||
load 5m
|
||||
testmetric1{src="a",dst="b"} 0
|
||||
testmetric2{src="a",dst="b"} 1
|
||||
|
||||
eval_fail instant at 0m ceil({__name__=~'testmetric1|testmetric2'})
|
9
promql/testdata/operators.test
vendored
9
promql/testdata/operators.test
vendored
|
@ -367,3 +367,12 @@ eval instant at 5m metricA + ignoring() metricB
|
|||
|
||||
eval instant at 5m metricA + metricB
|
||||
{baz="meh"} 7
|
||||
|
||||
clear
|
||||
|
||||
# Test duplicate labelset in promql output.
|
||||
load 5m
|
||||
testmetric1{src="a",dst="b"} 0
|
||||
testmetric2{src="a",dst="b"} 1
|
||||
|
||||
eval_fail instant at 0m -{__name__=~'testmetric1|testmetric2'}
|
|
@ -140,6 +140,22 @@ func (vec Vector) String() string {
|
|||
return strings.Join(entries, "\n")
|
||||
}
|
||||
|
||||
// ContainsSameLabelset checks if a vector has samples with the same labelset
|
||||
// Such a behaviour is semantically undefined
|
||||
// https://github.com/prometheus/prometheus/issues/4562
|
||||
func (vec Vector) ContainsSameLabelset() bool {
|
||||
l := make(map[uint64]struct{}, len(vec))
|
||||
for _, s := range vec {
|
||||
hash := s.Metric.Hash()
|
||||
if _, ok := l[hash]; ok {
|
||||
return true
|
||||
} else {
|
||||
l[hash] = struct{}{}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Matrix is a slice of Seriess that implements sort.Interface and
|
||||
// has a String method.
|
||||
type Matrix []Series
|
||||
|
@ -159,6 +175,22 @@ func (m Matrix) Len() int { return len(m) }
|
|||
func (m Matrix) Less(i, j int) bool { return labels.Compare(m[i].Metric, m[j].Metric) < 0 }
|
||||
func (m Matrix) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
|
||||
|
||||
// ContainsSameLabelset checks if a matrix has samples with the same labelset
|
||||
// Such a behaviour is semantically undefined
|
||||
// https://github.com/prometheus/prometheus/issues/4562
|
||||
func (m Matrix) ContainsSameLabelset() bool {
|
||||
l := make(map[uint64]struct{}, len(m))
|
||||
for _, ss := range m {
|
||||
hash := ss.Metric.Hash()
|
||||
if _, ok := l[hash]; ok {
|
||||
return true
|
||||
} else {
|
||||
l[hash] = struct{}{}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Result holds the resulting value of an execution or an error
|
||||
// if any occurred.
|
||||
type Result struct {
|
||||
|
|
Loading…
Reference in a new issue