Skip to content

Commit

Permalink
add get trigger checks function and merge (#944)
Browse files Browse the repository at this point in the history
  • Loading branch information
almostinf authored Nov 13, 2023
1 parent e509234 commit 342211a
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 0 deletions.
22 changes: 22 additions & 0 deletions database/redis/last_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,28 @@ func (connector *DbConnector) checkDataScoreChanged(triggerID string, checkData
return oldScore != float64(checkData.Score)
}

// getTriggersLastCheck returns an array of trigger checks by the passed ids, if the trigger does not exist, it is nil
func (connector *DbConnector) getTriggersLastCheck(triggerIDs []string) ([]*moira.CheckData, error) {
ctx := connector.context
pipe := (*connector.client).TxPipeline()

results := make([]*redis.StringCmd, len(triggerIDs))
for i, id := range triggerIDs {
var result *redis.StringCmd
if id != "" {
result = pipe.Get(ctx, metricLastCheckKey(id))
}
results[i] = result
}

_, err := pipe.Exec(ctx)
if err != nil && err != redis.Nil {
return nil, err
}

return reply.Checks(results)
}

var badStateTriggersKey = "moira-bad-state-triggers"
var triggersChecksKey = "moira-triggers-checks"

Expand Down
101 changes: 101 additions & 0 deletions database/redis/last_check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,107 @@ func TestLastCheckErrorConnection(t *testing.T) {
})
}

func TestGetTriggersLastCheck(t *testing.T) {
logger, _ := logging.GetLogger("dataBase")
dataBase := NewTestDatabase(logger)
dataBase.Flush()
defer dataBase.Flush()

_ = dataBase.SetTriggerLastCheck("test1", &moira.CheckData{
Timestamp: 1,
}, moira.TriggerSourceNotSet)

_ = dataBase.SetTriggerLastCheck("test2", &moira.CheckData{
Timestamp: 2,
}, moira.TriggerSourceNotSet)

_ = dataBase.SetTriggerLastCheck("test3", &moira.CheckData{
Timestamp: 3,
}, moira.TriggerSourceNotSet)

Convey("getTriggersLastCheck manipulations", t, func() {
Convey("Test with nil id array", func() {
actual, err := dataBase.getTriggersLastCheck(nil)
So(err, ShouldBeNil)
So(actual, ShouldResemble, []*moira.CheckData{})
})

Convey("Test with correct id array", func() {
actual, err := dataBase.getTriggersLastCheck([]string{"test1", "test2", "test3"})
So(err, ShouldBeNil)
So(actual, ShouldResemble, []*moira.CheckData{
{
Timestamp: 1,
MetricsToTargetRelation: map[string]string{},
},
{
Timestamp: 2,
MetricsToTargetRelation: map[string]string{},
},
{
Timestamp: 3,
MetricsToTargetRelation: map[string]string{},
},
})
})

Convey("Test with deleted trigger", func() {
dataBase.RemoveTriggerLastCheck("test2") //nolint
defer func() {
_ = dataBase.SetTriggerLastCheck("test2", &moira.CheckData{
Timestamp: 2,
}, moira.TriggerSourceNotSet)
}()

actual, err := dataBase.getTriggersLastCheck([]string{"test1", "test2", "test3"})
So(err, ShouldBeNil)
So(actual, ShouldResemble, []*moira.CheckData{
{
Timestamp: 1,
MetricsToTargetRelation: map[string]string{},
},
nil,
{
Timestamp: 3,
MetricsToTargetRelation: map[string]string{},
},
})
})

Convey("Test with a nonexistent trigger id", func() {
actual, err := dataBase.getTriggersLastCheck([]string{"test1", "test2", "test4"})
So(err, ShouldBeNil)
So(actual, ShouldResemble, []*moira.CheckData{
{
Timestamp: 1,
MetricsToTargetRelation: map[string]string{},
},
{
Timestamp: 2,
MetricsToTargetRelation: map[string]string{},
},
nil,
})
})

Convey("Test with an empty trigger id", func() {
actual, err := dataBase.getTriggersLastCheck([]string{"", "test2", "test3"})
So(err, ShouldBeNil)
So(actual, ShouldResemble, []*moira.CheckData{
nil,
{
Timestamp: 2,
MetricsToTargetRelation: map[string]string{},
},
{
Timestamp: 3,
MetricsToTargetRelation: map[string]string{},
},
})
})
})
}

func TestMaintenanceUserSave(t *testing.T) {
logger, _ := logging.GetLogger("dataBase")
dataBase := NewTestDatabase(logger)
Expand Down
21 changes: 21 additions & 0 deletions database/redis/reply/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,27 @@ func Check(rep *redis.StringCmd) (moira.CheckData, error) {
return checkSE.toCheckData(), nil
}

// Checks converts an array of redis DB reply to moira.CheckData objects, if reply is nil, then checkdata is nil
func Checks(replies []*redis.StringCmd) ([]*moira.CheckData, error) {
checks := make([]*moira.CheckData, len(replies))

for i, value := range replies {
if value != nil {
check, err := Check(value)
if err != nil {
if err != database.ErrNil {
return nil, err
}
continue
}

checks[i] = &check
}
}

return checks, nil
}

// GetCheckBytes is a function that takes moira.CheckData and turns it to bytes that will be saved in redis.
func GetCheckBytes(check moira.CheckData) ([]byte, error) {
checkSE := toCheckDataStorageElement(check)
Expand Down
10 changes: 10 additions & 0 deletions datatypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,16 @@ type ScheduledNotification struct {
CreatedAt int64 `json:"created_at" example:"1594471900" format:"int64"`
}

// Less is needed for the ScheduledNotification to match the Comparable interface
func (notification *ScheduledNotification) Less(other Comparable) (bool, error) {
otherNotification, ok := other.(*ScheduledNotification)
if !ok {
return false, fmt.Errorf("cannot to compare ScheduledNotification with different type")
}

return notification.Timestamp < otherNotification.Timestamp, nil
}

// MatchedMetric represents parsed and matched metric data
type MatchedMetric struct {
Metric string
Expand Down
26 changes: 26 additions & 0 deletions datatypes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -724,3 +724,29 @@ func testMaintenance(conveyMessage string, actualInfo MaintenanceInfo, maintenan
So(lastCheckTest.Maintenance, ShouldEqual, maintenance)
})
}

func TestScheduledNotificationLess(t *testing.T) {
Convey("Test Scheduled notification Less function", t, func() {
notification := &ScheduledNotification{
Timestamp: 5,
}

Convey("Test Less with nil", func() {
actual, err := notification.Less(nil)
So(err, ShouldResemble, fmt.Errorf("cannot to compare ScheduledNotification with different type"))
So(actual, ShouldBeFalse)
})

Convey("Test Less with less notification :)", func() {
actual, err := notification.Less(&ScheduledNotification{Timestamp: 1})
So(err, ShouldBeNil)
So(actual, ShouldBeFalse)
})

Convey("Test Less with greater notification", func() {
actual, err := notification.Less(&ScheduledNotification{Timestamp: 10})
So(err, ShouldBeNil)
So(actual, ShouldBeTrue)
})
})
}
37 changes: 37 additions & 0 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,40 @@ func ReplaceSubstring(str, begin, end, replaced string) string {
}
return result
}

type Comparable interface {
Less(other Comparable) (bool, error)
}

// Merge is a generic function that performs a merge of two sorted arrays into one sorted array
func MergeToSorted[T Comparable](arr1, arr2 []T) ([]T, error) {
merged := make([]T, 0, len(arr1)+len(arr2))
i, j := 0, 0

for i < len(arr1) && j < len(arr2) {
less, err := arr1[i].Less(arr2[j])
if err != nil {
return nil, err
}

if less {
merged = append(merged, arr1[i])
i++
} else {
merged = append(merged, arr2[j])
j++
}
}

for i < len(arr1) {
merged = append(merged, arr1[i])
i++
}

for j < len(arr2) {
merged = append(merged, arr2[j])
j++
}

return merged, nil
}
75 changes: 75 additions & 0 deletions helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,78 @@ func TestReplaceSubstring(t *testing.T) {
})
})
}

type myInt int

func (m myInt) Less(other Comparable) (bool, error) {
otherInt := other.(myInt)
return m < otherInt, nil
}

type myTest struct {
value int
}

func (test myTest) Less(other Comparable) (bool, error) {
otherTest := other.(myTest)
return test.value < otherTest.value, nil
}

func TestMergeToSorted(t *testing.T) {
Convey("Test MergeToSorted function", t, func() {
Convey("Test with two nil arrays", func() {
merged, err := MergeToSorted[myInt](nil, nil)
So(err, ShouldBeNil)
So(merged, ShouldResemble, []myInt{})
})

Convey("Test with one nil array", func() {
merged, err := MergeToSorted[myInt](nil, []myInt{1, 2, 3})
So(err, ShouldBeNil)
So(merged, ShouldResemble, []myInt{1, 2, 3})
})

Convey("Test with two arrays", func() {
merged, err := MergeToSorted[myInt]([]myInt{4, 5}, []myInt{1, 2, 3})
So(err, ShouldBeNil)
So(merged, ShouldResemble, []myInt{1, 2, 3, 4, 5})
})

Convey("Test with empty array", func() {
merged, err := MergeToSorted[myInt]([]myInt{-4, 5}, []myInt{})
So(err, ShouldBeNil)
So(merged, ShouldResemble, []myInt{-4, 5})
})

Convey("Test with sorted values but mixed up", func() {
merged, err := MergeToSorted[myInt]([]myInt{1, 9, 10}, []myInt{4, 8, 12})
So(err, ShouldBeNil)
So(merged, ShouldResemble, []myInt{1, 4, 8, 9, 10, 12})
})

Convey("Test with structure type", func() {
arr1 := []myTest{
{
value: 1,
},
{
value: 2,
},
}

arr2 := []myTest{
{
value: -2,
},
{
value: -1,
},
}

expected := append(arr2, arr1...)
merged, err := MergeToSorted[myTest](arr1, arr2)
So(err, ShouldBeNil)
So(merged, ShouldResemble, expected)
})
})
}

0 comments on commit 342211a

Please sign in to comment.