mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-26 22:19:40 -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.
|
// Make the function call.
|
||||||
enh.ts = ts
|
enh.ts = ts
|
||||||
result := f(args, enh)
|
result := f(args, enh)
|
||||||
|
if result.ContainsSameLabelset() {
|
||||||
|
ev.errorf("vector cannot contain metrics with the same labelset")
|
||||||
|
}
|
||||||
enh.out = result[:0] // Reuse result vector.
|
enh.out = result[:0] // Reuse result vector.
|
||||||
// If this could be an instant query, shortcut so as not to change sort order.
|
// If this could be an instant query, shortcut so as not to change sort order.
|
||||||
if ev.endTimestamp == ev.startTimestamp {
|
if ev.endTimestamp == ev.startTimestamp {
|
||||||
|
@ -883,6 +886,10 @@ func (ev *evaluator) eval(expr Expr) Value {
|
||||||
mat = append(mat, ss)
|
mat = append(mat, ss)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if mat.ContainsSameLabelset() {
|
||||||
|
ev.errorf("vector cannot contain metrics with the same labelset")
|
||||||
|
}
|
||||||
|
|
||||||
putPointSlice(points)
|
putPointSlice(points)
|
||||||
return mat
|
return mat
|
||||||
|
|
||||||
|
@ -898,6 +905,9 @@ func (ev *evaluator) eval(expr Expr) Value {
|
||||||
mat[i].Points[j].V = -mat[i].Points[j].V
|
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
|
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))
|
enh.dmn = make(map[uint64]labels.Labels, len(enh.out))
|
||||||
}
|
}
|
||||||
|
|
||||||
outSet := make(map[uint64]struct{}, len(vector))
|
|
||||||
for _, el := range vector {
|
for _, el := range vector {
|
||||||
h := el.Metric.Hash()
|
h := el.Metric.Hash()
|
||||||
var outMetric labels.Labels
|
var outMetric labels.Labels
|
||||||
|
@ -759,17 +758,10 @@ func funcLabelReplace(vals []Value, args Expressions, enh *EvalNodeHelper) Vecto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outHash := outMetric.Hash()
|
enh.out = append(enh.out, Sample{
|
||||||
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{
|
|
||||||
Metric: outMetric,
|
Metric: outMetric,
|
||||||
Point: Point{V: el.Point.V},
|
Point: Point{V: el.Point.V},
|
||||||
})
|
})
|
||||||
outSet[outHash] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return enh.out
|
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))
|
panic(fmt.Errorf("invalid destination label name in label_join(): %s", dst))
|
||||||
}
|
}
|
||||||
|
|
||||||
outSet := make(map[uint64]struct{}, len(vector))
|
|
||||||
srcVals := make([]string, len(srcLabels))
|
srcVals := make([]string, len(srcLabels))
|
||||||
for _, el := range vector {
|
for _, el := range vector {
|
||||||
h := el.Metric.Hash()
|
h := el.Metric.Hash()
|
||||||
|
@ -833,17 +824,11 @@ func funcLabelJoin(vals []Value, args Expressions, enh *EvalNodeHelper) Vector {
|
||||||
outMetric = lb.Labels()
|
outMetric = lb.Labels()
|
||||||
enh.dmn[h] = outMetric
|
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{
|
enh.out = append(enh.out, Sample{
|
||||||
Metric: outMetric,
|
Metric: outMetric,
|
||||||
Point: Point{V: el.Point.V},
|
Point: Point{V: el.Point.V},
|
||||||
})
|
})
|
||||||
outSet[outHash] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return enh.out
|
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))
|
eval instant at 0m days_in_month(vector(1485907200))
|
||||||
{} 28
|
{} 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
|
eval instant at 0h testmetric
|
||||||
testmetric{aa="bb"} 1
|
testmetric{aa="bb"} 1
|
||||||
testmetric{a="abb"} 2
|
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
|
eval instant at 5m metricA + metricB
|
||||||
{baz="meh"} 7
|
{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")
|
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
|
// Matrix is a slice of Seriess that implements sort.Interface and
|
||||||
// has a String method.
|
// has a String method.
|
||||||
type Matrix []Series
|
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) 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] }
|
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
|
// Result holds the resulting value of an execution or an error
|
||||||
// if any occurred.
|
// if any occurred.
|
||||||
type Result struct {
|
type Result struct {
|
||||||
|
|
Loading…
Reference in a new issue