Alerting: Add store level pagination of rules (#108633)

This commit is contained in:
William Wernert
2025-08-01 12:54:13 -04:00
committed by GitHub
parent 7e47968939
commit 2f0190d775
6 changed files with 536 additions and 51 deletions
+1
View File
@@ -23,6 +23,7 @@ type RuleStore interface {
GetAlertRuleByUID(ctx context.Context, query *ngmodels.GetAlertRuleByUIDQuery) (*ngmodels.AlertRule, error)
GetAlertRulesGroupByRuleUID(ctx context.Context, query *ngmodels.GetAlertRulesGroupByRuleUIDQuery) ([]*ngmodels.AlertRule, error)
ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error)
ListAlertRulesByGroup(ctx context.Context, query *ngmodels.ListAlertRulesByGroupQuery) (ngmodels.RulesGroup, string, error)
ListDeletedRules(ctx context.Context, orgID int64) ([]*ngmodels.AlertRule, error)
// InsertAlertRules will insert all alert rules passed into the function
@@ -2,7 +2,6 @@ package api
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
@@ -30,7 +29,7 @@ import (
type RuleStoreReader interface {
GetUserVisibleNamespaces(context.Context, int64, identity.Requester) (map[string]*folder.Folder, error)
ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error)
ListAlertRulesStore
}
type RuleGroupAccessControlService interface {
@@ -241,7 +240,7 @@ type RuleGroupStatusesOptions struct {
}
type ListAlertRulesStore interface {
ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error)
ListAlertRulesByGroup(ctx context.Context, query *ngmodels.ListAlertRulesByGroupQuery) (ngmodels.RulesGroup, string, error)
}
func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) response.Response {
@@ -476,15 +475,24 @@ func PrepareRuleGroupStatuses(log log.Logger, store ListAlertRulesStore, opts Ru
receiverName := opts.Query.Get("receiver_name")
alertRuleQuery := ngmodels.ListAlertRulesQuery{
OrgID: opts.OrgID,
NamespaceUIDs: namespaceUIDs,
DashboardUID: dashboardUID,
PanelID: panelID,
RuleGroups: ruleGroups,
ReceiverName: receiverName,
maxGroups := getInt64WithDefault(opts.Query, "group_limit", -1)
nextToken := opts.Query.Get("group_next_token")
if maxGroups == 0 {
return ruleResponse
}
ruleList, err := store.ListAlertRules(opts.Ctx, &alertRuleQuery)
byGroupQuery := ngmodels.ListAlertRulesByGroupQuery{
OrgID: opts.OrgID,
GroupLimit: maxGroups,
GroupContinueToken: nextToken,
NamespaceUIDs: namespaceUIDs,
DashboardUID: dashboardUID,
PanelID: panelID,
RuleGroups: ruleGroups,
ReceiverName: receiverName,
}
ruleList, continueToken, err := store.ListAlertRulesByGroup(opts.Ctx, &byGroupQuery)
if err != nil {
ruleResponse.Status = "error"
ruleResponse.Error = fmt.Sprintf("failure getting rules: %s", err.Error())
@@ -498,31 +506,9 @@ func PrepareRuleGroupStatuses(log log.Logger, store ListAlertRulesStore, opts Ru
ruleNamesSet[rn] = struct{}{}
}
maxGroups := getInt64WithDefault(opts.Query, "group_limit", -1)
nextToken := opts.Query.Get("group_next_token")
if nextToken != "" {
if _, err := base64.URLEncoding.DecodeString(nextToken); err != nil {
nextToken = ""
}
}
groupedRules := getGroupedRules(log, ruleList, ruleNamesSet, opts.AllowedNamespaces)
rulesTotals := make(map[string]int64, len(groupedRules))
var newToken string
foundToken := false
for _, rg := range groupedRules {
if nextToken != "" && !foundToken {
if !tokenGreaterThanOrEqual(getRuleGroupNextToken(rg.Folder, rg.GroupKey.RuleGroup), nextToken) {
continue
}
foundToken = true
}
if maxGroups > -1 && len(ruleResponse.Data.RuleGroups) == int(maxGroups) {
newToken = getRuleGroupNextToken(rg.Folder, rg.GroupKey.RuleGroup)
break
}
ruleGroup, totals := toRuleGroup(log, rg.GroupKey, rg.Folder, rg.Rules, provenanceRecords, limitAlertsPerRule, stateFilterSet, matchers, labelOptions, ruleStatusMutator, alertStateMutator)
ruleGroup.Totals = totals
for k, v := range totals {
@@ -546,7 +532,7 @@ func PrepareRuleGroupStatuses(log log.Logger, store ListAlertRulesStore, opts Ru
}
}
ruleResponse.Data.NextToken = newToken
ruleResponse.Data.NextToken = continueToken
// Only return Totals if there is no pagination
if maxGroups == -1 {
@@ -556,18 +542,6 @@ func PrepareRuleGroupStatuses(log log.Logger, store ListAlertRulesStore, opts Ru
return ruleResponse
}
func getRuleGroupNextToken(namespace, group string) string {
return base64.URLEncoding.EncodeToString([]byte(namespace + "/" + group))
}
// Returns true if tokenA >= tokenB
func tokenGreaterThanOrEqual(tokenA string, tokenB string) bool {
decodedTokenA, _ := base64.URLEncoding.DecodeString(tokenA)
decodedTokenB, _ := base64.URLEncoding.DecodeString(tokenB)
return string(decodedTokenA) >= string(decodedTokenB)
}
type ruleGroup struct {
Folder string
GroupKey ngmodels.AlertRuleGroupKey