promtool: Speed up checking for duplicate rules

Trade space for speed. Convert all rules into our temporary struct, sort
and then iterate. This is a significant when having many rules.

Signed-off-by: Holger Hans Peter Freyther <holger@moiji-mobile.com>
pull/9306/head
Holger Hans Peter Freyther 3 years ago
parent 3a309c1ae5
commit 5edec40d60

@ -24,7 +24,7 @@ import (
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "sort"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -48,6 +48,7 @@ import (
_ "github.com/prometheus/prometheus/discovery/install" // Register service discovery implementations. _ "github.com/prometheus/prometheus/discovery/install" // Register service discovery implementations.
"github.com/prometheus/prometheus/discovery/kubernetes" "github.com/prometheus/prometheus/discovery/kubernetes"
"github.com/prometheus/prometheus/discovery/targetgroup" "github.com/prometheus/prometheus/discovery/targetgroup"
"github.com/prometheus/prometheus/pkg/labels"
"github.com/prometheus/prometheus/pkg/rulefmt" "github.com/prometheus/prometheus/pkg/rulefmt"
"github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/promql"
) )
@ -471,8 +472,8 @@ func checkRules(filename string) (int, []error) {
fmt.Printf("%d duplicate rule(s) found.\n", len(dRules)) fmt.Printf("%d duplicate rule(s) found.\n", len(dRules))
for _, n := range dRules { for _, n := range dRules {
fmt.Printf("Metric: %s\nLabel(s):\n", n.metric) fmt.Printf("Metric: %s\nLabel(s):\n", n.metric)
for i, l := range n.label { for _, l := range n.label {
fmt.Printf("\t%s: %s\n", i, l) fmt.Printf("\t%s: %s\n", l.Name, l.Value)
} }
} }
fmt.Println("Might cause inconsistency while recording expressions.") fmt.Println("Might cause inconsistency while recording expressions.")
@ -483,29 +484,52 @@ func checkRules(filename string) (int, []error) {
type compareRuleType struct { type compareRuleType struct {
metric string metric string
label map[string]string label labels.Labels
}
type compareRuleTypes []compareRuleType
func (c compareRuleTypes) Len() int { return len(c) }
func (c compareRuleTypes) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c compareRuleTypes) Less(i, j int) bool { return compare(c[i], c[j]) < 0 }
func compare(a, b compareRuleType) int {
if res := strings.Compare(a.metric, b.metric); res != 0 {
return res
}
return labels.Compare(a.label, b.label)
} }
func checkDuplicates(groups []rulefmt.RuleGroup) []compareRuleType { func checkDuplicates(groups []rulefmt.RuleGroup) []compareRuleType {
var duplicates []compareRuleType var duplicates []compareRuleType
var rules compareRuleTypes
for _, group := range groups { for _, group := range groups {
for index, rule := range group.Rules {
inst := compareRuleType{ for _, rule := range group.Rules {
rules = append(rules, compareRuleType{
metric: ruleMetric(rule), metric: ruleMetric(rule),
label: rule.Labels, label: labels.FromMap(rule.Labels),
} })
for i := 0; i < index; i++ { }
t := compareRuleType{ }
metric: ruleMetric(group.Rules[i]), if len(rules) < 2 {
label: group.Rules[i].Labels, return duplicates
} }
if reflect.DeepEqual(t, inst) { sort.Sort(rules)
duplicates = append(duplicates, t)
} last := rules[0]
for i := 1; i < len(rules); i++ {
if compare(last, rules[i]) == 0 {
// Don't add a duplicated rule multiple times.
if len(duplicates) == 0 || compare(last, duplicates[len(duplicates)-1]) != 0 {
duplicates = append(duplicates, rules[i])
} }
} }
last = rules[i]
} }
return duplicates return duplicates
} }

@ -21,6 +21,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/prometheus/prometheus/pkg/labels"
"github.com/prometheus/prometheus/pkg/rulefmt" "github.com/prometheus/prometheus/pkg/rulefmt"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -136,6 +137,7 @@ func TestCheckDuplicates(t *testing.T) {
expectedDups: []compareRuleType{ expectedDups: []compareRuleType{
{ {
metric: "job:test:count_over_time1m", metric: "job:test:count_over_time1m",
label: labels.New(),
}, },
}, },
}, },

Loading…
Cancel
Save