mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-01 14:47:38 +00:00
vmalert: use stringified label keys for duplicates map in recroding rules (#1301)
duplicates map helps to determine wheter extra labels has overriden labels which make time series unique. It was using a sorted hashed labels sequence as a key. But hashing algorithm could have collisions, so it is more convenient to not use hashing at all. Log message for recording rules duplicates was improved as well. https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1293
This commit is contained in:
parent
9c8a411e08
commit
3428df6f15
1 changed files with 21 additions and 16 deletions
|
@ -3,8 +3,8 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/fnv"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -103,33 +103,38 @@ func (rr *RecordingRule) Exec(ctx context.Context, series bool) ([]prompbmarshal
|
||||||
return nil, fmt.Errorf("failed to execute query %q: %w", rr.Expr, err)
|
return nil, fmt.Errorf("failed to execute query %q: %w", rr.Expr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
duplicates := make(map[uint64]prompbmarshal.TimeSeries, len(qMetrics))
|
duplicates := make(map[string]struct{}, len(qMetrics))
|
||||||
var tss []prompbmarshal.TimeSeries
|
var tss []prompbmarshal.TimeSeries
|
||||||
for _, r := range qMetrics {
|
for _, r := range qMetrics {
|
||||||
ts := rr.toTimeSeries(r, time.Unix(r.Timestamp, 0))
|
ts := rr.toTimeSeries(r, time.Unix(r.Timestamp, 0))
|
||||||
h := hashTimeSeries(ts)
|
key := stringifyLabels(ts)
|
||||||
if _, ok := duplicates[h]; ok {
|
if _, ok := duplicates[key]; ok {
|
||||||
rr.lastExecError = errDuplicate
|
rr.lastExecError = errDuplicate
|
||||||
return nil, errDuplicate
|
return nil, fmt.Errorf("original metric %v; resulting labels %q: %w", r, key, errDuplicate)
|
||||||
}
|
}
|
||||||
duplicates[h] = ts
|
duplicates[key] = struct{}{}
|
||||||
tss = append(tss, ts)
|
tss = append(tss, ts)
|
||||||
}
|
}
|
||||||
return tss, nil
|
return tss, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashTimeSeries(ts prompbmarshal.TimeSeries) uint64 {
|
func stringifyLabels(ts prompbmarshal.TimeSeries) string {
|
||||||
hash := fnv.New64a()
|
|
||||||
labels := ts.Labels
|
labels := ts.Labels
|
||||||
|
if len(labels) > 1 {
|
||||||
sort.Slice(labels, func(i, j int) bool {
|
sort.Slice(labels, func(i, j int) bool {
|
||||||
return labels[i].Name < labels[j].Name
|
return labels[i].Name < labels[j].Name
|
||||||
})
|
})
|
||||||
for _, l := range labels {
|
|
||||||
hash.Write([]byte(l.Name))
|
|
||||||
hash.Write([]byte(l.Value))
|
|
||||||
hash.Write([]byte("\xff"))
|
|
||||||
}
|
}
|
||||||
return hash.Sum64()
|
b := strings.Builder{}
|
||||||
|
for i, l := range labels {
|
||||||
|
b.WriteString(l.Name)
|
||||||
|
b.WriteString("=")
|
||||||
|
b.WriteString(l.Value)
|
||||||
|
if i != len(labels)-1 {
|
||||||
|
b.WriteString(",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *RecordingRule) toTimeSeries(m datasource.Metric, timestamp time.Time) prompbmarshal.TimeSeries {
|
func (rr *RecordingRule) toTimeSeries(m datasource.Metric, timestamp time.Time) prompbmarshal.TimeSeries {
|
||||||
|
|
Loading…
Reference in a new issue