mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-15 00:13:30 +01:00
e5f5342e18
Configuration reload and rules evaluation can't be executed in same time now. This may make reload time longer but prevents from potential races.
164 lines
4.1 KiB
Go
164 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"math/rand"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestManagerUpdateError(t *testing.T) {
|
|
m := &manager{groups: make(map[uint64]*Group)}
|
|
path := []string{"foo/bar"}
|
|
err := m.update(context.Background(), path, true, false)
|
|
if err == nil {
|
|
t.Fatalf("expected to have err; got nil instead")
|
|
}
|
|
expErr := "no groups found"
|
|
if !strings.Contains(err.Error(), expErr) {
|
|
t.Fatalf("expected to got err %s; got %s", expErr, err)
|
|
}
|
|
}
|
|
|
|
// TestManagerUpdateConcurrent supposed to test concurrent
|
|
// execution of configuration update.
|
|
// Should be executed with -race flag
|
|
func TestManagerUpdateConcurrent(t *testing.T) {
|
|
m := &manager{
|
|
groups: make(map[uint64]*Group),
|
|
storage: &fakeQuerier{},
|
|
notifier: &fakeNotifier{},
|
|
}
|
|
paths := []string{
|
|
"testdata/dir/rules0-good.rules",
|
|
"testdata/dir/rules1-good.rules",
|
|
"testdata/rules0-good.rules",
|
|
}
|
|
*evaluationInterval = time.Millisecond
|
|
if err := m.start(context.Background(), []string{paths[0]}, true); err != nil {
|
|
t.Fatalf("failed to start: %s", err)
|
|
}
|
|
|
|
const workers = 500
|
|
const iterations = 10
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(workers)
|
|
for i := 0; i < workers; i++ {
|
|
go func() {
|
|
defer wg.Done()
|
|
for i := 0; i < iterations; i++ {
|
|
rnd := rand.Intn(len(paths))
|
|
path := []string{paths[rnd]}
|
|
err := m.update(context.Background(), path, true, false)
|
|
if err != nil {
|
|
t.Errorf("update error: %s", err)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
// TestManagerUpdate tests sequential configuration
|
|
// updates.
|
|
func TestManagerUpdate(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
initPath string
|
|
updatePath string
|
|
want []*Group
|
|
}{
|
|
{
|
|
name: "update good rules",
|
|
initPath: "testdata/rules0-good.rules",
|
|
updatePath: "testdata/dir/rules1-good.rules",
|
|
want: []*Group{
|
|
{
|
|
File: "testdata/dir/rules1-good.rules",
|
|
Name: "duplicatedGroupDiffFiles",
|
|
Rules: []*Rule{newTestRule("VMRows", time.Second*10)},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update good rules from 1 to 2 groups",
|
|
initPath: "testdata/dir/rules1-good.rules",
|
|
updatePath: "testdata/rules0-good.rules",
|
|
want: []*Group{
|
|
{
|
|
File: "testdata/rules0-good.rules",
|
|
Name: "groupGorSingleAlert", Rules: []*Rule{
|
|
newTestRule("VMRows", time.Second*10),
|
|
}},
|
|
{
|
|
File: "testdata/rules0-good.rules",
|
|
Name: "TestGroup", Rules: []*Rule{
|
|
newTestRule("Conns", time.Duration(0)),
|
|
newTestRule("ExampleAlertAlwaysFiring", time.Duration(0)),
|
|
}},
|
|
},
|
|
},
|
|
{
|
|
name: "update with one bad rule file",
|
|
initPath: "testdata/rules0-good.rules",
|
|
updatePath: "testdata/dir/rules2-bad.rules",
|
|
want: []*Group{
|
|
{
|
|
File: "testdata/rules0-good.rules",
|
|
Name: "groupGorSingleAlert", Rules: []*Rule{
|
|
newTestRule("VMRows", time.Second*10),
|
|
}},
|
|
{
|
|
File: "testdata/rules0-good.rules",
|
|
Name: "TestGroup", Rules: []*Rule{
|
|
newTestRule("Conns", time.Duration(0)),
|
|
newTestRule("ExampleAlertAlwaysFiring", time.Duration(0)),
|
|
}},
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.TODO())
|
|
m := &manager{groups: make(map[uint64]*Group), storage: &fakeQuerier{}}
|
|
path := []string{tc.initPath}
|
|
if err := m.update(ctx, path, true, false); err != nil {
|
|
t.Fatalf("failed to complete initial rules update: %s", err)
|
|
}
|
|
|
|
path = []string{tc.updatePath}
|
|
_ = m.update(ctx, path, true, false)
|
|
if len(tc.want) != len(m.groups) {
|
|
t.Fatalf("\nwant number of groups: %d;\ngot: %d ", len(tc.want), len(m.groups))
|
|
}
|
|
|
|
for _, wantG := range tc.want {
|
|
gotG, ok := m.groups[wantG.ID()]
|
|
if !ok {
|
|
t.Fatalf("expected to have group %q", wantG.Name)
|
|
}
|
|
compareGroups(t, gotG, wantG)
|
|
}
|
|
|
|
cancel()
|
|
m.close()
|
|
})
|
|
}
|
|
}
|
|
|
|
func compareGroups(t *testing.T, a, b *Group) {
|
|
t.Helper()
|
|
if len(a.Rules) != len(b.Rules) {
|
|
t.Fatalf("expected group %s to have %d rules; got: %d",
|
|
a.Name, len(a.Rules), len(b.Rules))
|
|
}
|
|
for i, r := range a.Rules {
|
|
got, want := r, b.Rules[i]
|
|
if got.Name != want.Name {
|
|
t.Fatalf("expected to have rule %q; got %q", want.Name, got.Name)
|
|
}
|
|
}
|
|
}
|