Alerting: Limit GMA alerts on the new list page (#105657)

This commit is contained in:
Konrad Lalik
2025-05-21 12:26:23 +02:00
committed by GitHub
parent 7361537253
commit e2cd5c870f
13 changed files with 42 additions and 23 deletions
@@ -28,10 +28,11 @@ type PromRulesOptions = WithNotificationOptions<{
groupNextToken?: string;
}>;
type GrafanaPromRulesOptions = Omit<PromRulesOptions, 'ruleSource' | 'namespace'> & {
type GrafanaPromRulesOptions = Omit<PromRulesOptions, 'ruleSource' | 'namespace' | 'excludeAlerts'> & {
folderUid?: string;
dashboardUid?: string;
panelId?: number;
limitAlerts?: number;
};
export const prometheusApi = alertingApi.injectEndpoints({
@@ -71,13 +72,13 @@ export const prometheusApi = alertingApi.injectEndpoints({
},
}),
getGrafanaGroups: build.query<PromRulesResponse<GrafanaPromRuleGroupDTO>, GrafanaPromRulesOptions>({
query: ({ folderUid, groupName, ruleName, groupLimit, excludeAlerts, groupNextToken }) => ({
query: ({ folderUid, groupName, ruleName, groupLimit, limitAlerts, groupNextToken }) => ({
url: `api/prometheus/grafana/api/v1/rules`,
params: {
folder_uid: folderUid,
rule_group: groupName,
rule_name: ruleName,
exclude_alerts: excludeAlerts?.toString(),
limit_alerts: limitAlerts,
group_limit: groupLimit?.toFixed(0),
group_next_token: groupNextToken,
},
@@ -1,4 +1,4 @@
import { isUndefined, omitBy, pick, sum } from 'lodash';
import { isUndefined, omitBy } from 'lodash';
import pluralize from 'pluralize';
import * as React from 'react';
import { Fragment, useDeferredValue, useMemo } from 'react';
@@ -13,6 +13,8 @@ import {
} from 'app/types/unified-alerting';
import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
import { totalFromStats } from '../../utils/ruleStats';
interface Props {
namespaces: CombinedRuleNamespace[];
}
@@ -80,15 +82,6 @@ function statsFromNamespaces(namespaces: CombinedRuleNamespace[]): AlertGroupTot
return stats;
}
export function totalFromStats(stats: AlertGroupTotals): number {
// countable stats will pick only the states that indicate a single rule – health indicators like "error" and "nodata" should
// not be counted because they are already counted by their state
const countableStats = pick(stats, ['alerting', 'pending', 'inactive', 'recording', 'recovering']);
const total = sum(Object.values(countableStats));
return total;
}
export const RuleGroupStats = ({ group }: RuleGroupStatsProps) => {
const stats = group.totals;
const evaluationInterval = group?.interval;
@@ -232,6 +232,8 @@ export const mockGrafanaPromAlertingRule = (
uid: 'mock-rule-uid-123',
folderUid: 'NAMESPACE_UID',
isPaused: false,
totals: { alerting: 1 },
totalsFiltered: { alerting: 1 },
...partial,
};
};
@@ -168,6 +168,8 @@ function rulerRuleToPromRule(rule: RulerGrafanaRuleDTO): GrafanaPromRuleDTO {
health: 'ok',
state: PromAlertingRuleState.Inactive,
type: rulerRuleType.grafana.alertingRule(rule) ? PromRuleType.Alerting : PromRuleType.Recording,
totals: {},
totalsFiltered: {},
};
}
@@ -44,6 +44,7 @@ export function GrafanaGroupLoader({
{
folderUid: groupIdentifier.namespace.uid,
groupName: groupIdentifier.groupName,
limitAlerts: 0,
},
{ pollingInterval: RULE_LIST_POLL_INTERVAL_MS }
);
@@ -6,6 +6,7 @@ import { GrafanaPromRuleDTO, PromRuleType, RulerGrafanaRuleDTO } from 'app/types
import { alertRuleApi } from '../api/alertRuleApi';
import { prometheusApi } from '../api/prometheusApi';
import { GrafanaRulesSource } from '../utils/datasource';
import { totalFromStats } from '../utils/ruleStats';
import { rulerRuleType } from '../utils/rules';
import { createRelativeUrl } from '../utils/url';
@@ -123,13 +124,14 @@ export function GrafanaRuleListItem({
if (rulerRuleType.grafana.alertingRule(rulerRule)) {
const promAlertingRule = rule && rule.type === PromRuleType.Alerting ? rule : undefined;
const instancesCount = totalFromStats(promAlertingRule?.totals ?? {});
return (
<AlertRuleListItem
{...commonProps}
summary={annotations.summary}
state={promAlertingRule?.state}
instancesCount={promAlertingRule?.alerts?.length}
instancesCount={instancesCount}
operation={operation}
/>
);
@@ -24,7 +24,7 @@ import { useLazyLoadPrometheusGroups } from './hooks/useLazyLoadPrometheusGroups
export const GRAFANA_GROUP_PAGE_SIZE = 40;
export function PaginatedGrafanaLoader() {
const grafanaGroupsGenerator = useGrafanaGroupsGenerator({ populateCache: true });
const grafanaGroupsGenerator = useGrafanaGroupsGenerator({ populateCache: true, limitAlerts: 0 });
const groupsGenerator = useRef(toIndividualRuleGroups(grafanaGroupsGenerator(GRAFANA_GROUP_PAGE_SIZE)));
@@ -11,6 +11,7 @@ const { useLazyGetGroupsQuery, useLazyGetGrafanaGroupsQuery } = prometheusApi;
interface UseGeneratorHookOptions {
populateCache?: boolean;
limitAlerts?: number;
}
interface FetchGroupsOptions {
@@ -58,7 +59,7 @@ export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions =
const getGroupsAndProvideCache = useCallback(
async (fetchOptions: FetchGroupsOptions) => {
const response = await getGrafanaGroups(fetchOptions).unwrap();
const response = await getGrafanaGroups({ ...fetchOptions, limitAlerts: hookOptions.limitAlerts }).unwrap();
// This is not mandatory to preload ruler rules, but it improves the UX
// Because the user waits a bit longer for the initial load but doesn't need to wait for each group to be loaded
@@ -74,7 +75,7 @@ export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions =
await dispatch(
prometheusApi.util.upsertQueryData(
'getGrafanaGroups',
{ folderUid: group.folderUid, groupName: group.name },
{ folderUid: group.folderUid, groupName: group.name, limitAlerts: hookOptions.limitAlerts },
{ data: { groups: [group] }, status: 'success' }
)
);
@@ -85,7 +86,7 @@ export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions =
return response;
},
[getGrafanaGroups, dispatch, hookOptions.populateCache]
[getGrafanaGroups, dispatch, hookOptions.populateCache, hookOptions.limitAlerts]
);
return useCallback(
@@ -56,7 +56,7 @@ export function useFilteredRulesIteratorProvider() {
const allExternalRulesSources = getExternalRulesSources();
const prometheusGroupsGenerator = usePrometheusGroupsGenerator();
const grafanaGroupsGenerator = useGrafanaGroupsGenerator();
const grafanaGroupsGenerator = useGrafanaGroupsGenerator({ limitAlerts: 0 });
const getFilteredRulesIterable = (filterState: RulesFilter, groupLimit: number): GetIteratorResult => {
/* this is the abort controller that allows us to stop an AsyncIterable */
@@ -1,6 +1,6 @@
import { totalFromStats } from './RuleStats';
import { totalFromStats } from './ruleStats';
describe('RuleStats', () => {
describe('totalFromStats', () => {
it('should count 0', () => {
expect(
totalFromStats({
@@ -0,0 +1,12 @@
import { pick, sum } from 'lodash';
import { AlertGroupTotals } from 'app/types/unified-alerting';
export function totalFromStats(stats: AlertGroupTotals): number {
// countable stats will pick only the states that indicate a single rule – health indicators like "error" and "nodata" should
// not be counted because they are already counted by their state
const countableStats = pick(stats, ['alerting', 'pending', 'inactive', 'recording', 'recovering']);
const total = sum(Object.values(countableStats));
return total;
}
@@ -87,6 +87,8 @@ export function getPrometheusRulesResponse(
evaluationTime: 0,
uid: rule_uid,
folderUid: folderUid,
totals: {},
totalsFiltered: {},
},
],
interval: 60,
+5 -2
View File
@@ -3,7 +3,7 @@
import { DataQuery, RelativeTimeRange } from '@grafana/data';
import { ExpressionQuery } from 'app/features/expressions/types';
import { AlertGroupTotals } from './unified-alerting';
import { AlertGroupTotals, AlertInstanceTotals } from './unified-alerting';
export type Labels = Record<string, string>;
export type Annotations = Record<string, string>;
@@ -170,7 +170,10 @@ export interface PromRuleGroupDTO<TRule = PromRuleDTO> {
lastEvaluation?: string;
}
export interface GrafanaPromAlertingRuleDTO extends GrafanaPromRuleDTOBase, PromAlertingRuleDTO {}
export interface GrafanaPromAlertingRuleDTO extends GrafanaPromRuleDTOBase, PromAlertingRuleDTO {
totals: AlertInstanceTotals;
totalsFiltered: AlertInstanceTotals;
}
export interface GrafanaPromRecordingRuleDTO extends GrafanaPromRuleDTOBase, PromRecordingRuleDTO {}