diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index 6bf3c088421..12fde52f4c6 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -113,6 +113,13 @@ type AlertRule struct { Labels map[string]string } +type SchedulableAlertRule struct { + UID string `xorm:"uid"` + OrgID int64 `xorm:"org_id"` + IntervalSeconds int64 + Version int64 +} + type LabelOption func(map[string]string) func WithoutInternalLabels() LabelOption { @@ -169,6 +176,11 @@ func (alertRule *AlertRule) GetKey() AlertRuleKey { return AlertRuleKey{OrgID: alertRule.OrgID, UID: alertRule.UID} } +// GetKey returns the alert definitions identifier +func (alertRule *SchedulableAlertRule) GetKey() AlertRuleKey { + return AlertRuleKey{OrgID: alertRule.OrgID, UID: alertRule.UID} +} + // PreSave sets default values and loads the updated model for each alert query. func (alertRule *AlertRule) PreSave(timeNow func() time.Time) error { for i, q := range alertRule.Data { @@ -242,6 +254,12 @@ type ListAlertRulesQuery struct { Result []*AlertRule } +type GetAlertRulesForSchedulingQuery struct { + ExcludeOrgIDs []int64 + + Result []*SchedulableAlertRule +} + // ListNamespaceAlertRulesQuery is the query for listing namespace alert rules type ListNamespaceAlertRulesQuery struct { OrgID int64 diff --git a/pkg/services/ngalert/schedule/fetcher.go b/pkg/services/ngalert/schedule/fetcher.go index 9f20f4ac23d..7a2cf1d55b8 100644 --- a/pkg/services/ngalert/schedule/fetcher.go +++ b/pkg/services/ngalert/schedule/fetcher.go @@ -7,14 +7,14 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/models" ) -func (sch *schedule) getAlertRules(ctx context.Context, disabledOrgs []int64) []*models.AlertRule { +func (sch *schedule) getAlertRules(ctx context.Context, disabledOrgs []int64) []*models.SchedulableAlertRule { start := time.Now() defer func() { sch.metrics.GetAlertRulesDuration.Observe(time.Since(start).Seconds()) }() - q := models.ListAlertRulesQuery{ - ExcludeOrgs: disabledOrgs, + q := models.GetAlertRulesForSchedulingQuery{ + ExcludeOrgIDs: disabledOrgs, } err := sch.ruleStore.GetAlertRulesForScheduling(ctx, &q) if err != nil { diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index bc6a0f97c00..0402942bc8e 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -37,7 +37,7 @@ type RuleStore interface { DeleteAlertRulesByUID(ctx context.Context, orgID int64, ruleUID ...string) error DeleteAlertInstancesByRuleUID(ctx context.Context, orgID int64, ruleUID string) error GetAlertRuleByUID(ctx context.Context, query *ngmodels.GetAlertRuleByUIDQuery) error - GetAlertRulesForScheduling(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error + GetAlertRulesForScheduling(ctx context.Context, query *ngmodels.GetAlertRulesForSchedulingQuery) error ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error // GetRuleGroups returns the unique rule groups across all organizations. GetRuleGroups(ctx context.Context, query *ngmodels.ListRuleGroupsQuery) error @@ -344,16 +344,19 @@ func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, org return folder, nil } -// GetAlertRulesForScheduling returns alert rule info (identifier, interval, version state) -// that is useful for it's scheduling. -func (st DBstore) GetAlertRulesForScheduling(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error { +// GetAlertRulesForScheduling returns a short version of all alert rules except those that belong to an excluded list of organizations +func (st DBstore) GetAlertRulesForScheduling(ctx context.Context, query *ngmodels.GetAlertRulesForSchedulingQuery) error { return st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { - alerts := make([]*ngmodels.AlertRule, 0) - q := "SELECT uid, org_id, interval_seconds, version FROM alert_rule" - if len(query.ExcludeOrgs) > 0 { - q = fmt.Sprintf("%s WHERE org_id NOT IN (%s)", q, strings.Join(strings.Split(strings.Trim(fmt.Sprint(query.ExcludeOrgs), "[]"), " "), ",")) + alerts := make([]*ngmodels.SchedulableAlertRule, 0) + q := sess.Table("alert_rule") + if len(query.ExcludeOrgIDs) > 0 { + excludeOrgs := make([]interface{}, 0, len(query.ExcludeOrgIDs)) + for _, orgID := range query.ExcludeOrgIDs { + excludeOrgs = append(excludeOrgs, orgID) + } + q = q.NotIn("org_id", excludeOrgs...) } - if err := sess.SQL(q).Find(&alerts); err != nil { + if err := q.Find(&alerts); err != nil { return err } query.Result = alerts diff --git a/pkg/services/ngalert/store/testing.go b/pkg/services/ngalert/store/testing.go index 1a3af60bc30..a6cad95bcfb 100644 --- a/pkg/services/ngalert/store/testing.go +++ b/pkg/services/ngalert/store/testing.go @@ -153,7 +153,7 @@ func (f *FakeRuleStore) GetAlertRuleByUID(_ context.Context, q *models.GetAlertR } // For now, we're not implementing namespace filtering. -func (f *FakeRuleStore) GetAlertRulesForScheduling(_ context.Context, q *models.ListAlertRulesQuery) error { +func (f *FakeRuleStore) GetAlertRulesForScheduling(_ context.Context, q *models.GetAlertRulesForSchedulingQuery) error { f.mtx.Lock() defer f.mtx.Unlock() f.RecordedOps = append(f.RecordedOps, *q) @@ -161,7 +161,14 @@ func (f *FakeRuleStore) GetAlertRulesForScheduling(_ context.Context, q *models. return err } for _, rules := range f.Rules { - q.Result = append(q.Result, rules...) + for _, rule := range rules { + q.Result = append(q.Result, &models.SchedulableAlertRule{ + UID: rule.UID, + OrgID: rule.OrgID, + IntervalSeconds: rule.IntervalSeconds, + Version: rule.Version, + }) + } } return nil }