Alerting: Improve new list view performance (#108115)

kristina/correlation-migrate
Gilles De Mey 5 days ago committed by GitHub
parent c06c1b1e8a
commit 8cb6993fe6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 16
      public/app/features/alerting/unified/api/prometheusApi.ts
  2. 4
      public/app/features/alerting/unified/rule-list/PaginatedDataSourceLoader.tsx
  3. 38
      public/app/features/alerting/unified/rule-list/hooks/prometheusGroupsGenerator.ts
  4. 7
      public/app/features/alerting/unified/rule-list/hooks/useFilteredRulesIterator.ts

@ -118,18 +118,20 @@ export const prometheusApi = alertingApi.injectEndpoints({
export function usePopulateGrafanaPrometheusApiCache() { export function usePopulateGrafanaPrometheusApiCache() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const populateGroupResponseCache = useCallback( const populateGroupsResponseCache = useCallback(
(group: GrafanaPromRuleGroupDTO) => { (groups: GrafanaPromRuleGroupDTO[]) => {
dispatch( dispatch(
prometheusApi.util.upsertQueryData( prometheusApi.util.upsertQueryEntries(
'getGrafanaGroups', groups.map((group) => ({
{ folderUid: group.folderUid, groupName: group.name }, endpointName: 'getGrafanaGroups',
{ data: { groups: [group] }, status: 'success' } arg: { folderUid: group.folderUid, groupName: group.name, limitAlerts: 0 },
value: { data: { groups: [group] }, status: 'success' },
}))
) )
); );
}, },
[dispatch] [dispatch]
); );
return { populateGroupResponseCache }; return { populateGroupsResponseCache };
} }

@ -51,9 +51,7 @@ function PaginatedGroupsLoader({ rulesSourceIdentifier, application, groupFilter
const hasFilters = Boolean(groupFilter || namespaceFilter); const hasFilters = Boolean(groupFilter || namespaceFilter);
const { uid, name } = rulesSourceIdentifier; const { uid, name } = rulesSourceIdentifier;
const prometheusGroupsGenerator = usePrometheusGroupsGenerator({ const prometheusGroupsGenerator = usePrometheusGroupsGenerator();
populateCache: hasFilters ? false : true,
});
// If there are no filters we can match one frontend page to one API page. // If there are no filters we can match one frontend page to one API page.
// However, if there are filters, we need to fetch more groups from the API to populate one frontend page // However, if there are filters, we need to fetch more groups from the API to populate one frontend page

@ -1,10 +1,9 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useDispatch } from 'app/types/store';
import { DataSourceRulesSourceIdentifier, RuleHealth } from 'app/types/unified-alerting'; import { DataSourceRulesSourceIdentifier, RuleHealth } from 'app/types/unified-alerting';
import { PromAlertingRuleState, PromRuleGroupDTO } from 'app/types/unified-alerting-dto'; import { PromAlertingRuleState, PromRuleGroupDTO } from 'app/types/unified-alerting-dto';
import { PromRulesResponse, prometheusApi } from '../../api/prometheusApi'; import { PromRulesResponse, prometheusApi, usePopulateGrafanaPrometheusApiCache } from '../../api/prometheusApi';
const { useLazyGetGroupsQuery, useLazyGetGrafanaGroupsQuery } = prometheusApi; const { useLazyGetGroupsQuery, useLazyGetGrafanaGroupsQuery } = prometheusApi;
@ -22,8 +21,7 @@ interface FetchGroupsOptions {
groupNextToken?: string; groupNextToken?: string;
} }
export function usePrometheusGroupsGenerator(hookOptions: UseGeneratorHookOptions = {}) { export function usePrometheusGroupsGenerator() {
const dispatch = useDispatch();
const [getGroups] = useLazyGetGroupsQuery(); const [getGroups] = useLazyGetGroupsQuery();
return useCallback( return useCallback(
@ -35,24 +33,12 @@ export function usePrometheusGroupsGenerator(hookOptions: UseGeneratorHookOption
...fetchOptions, ...fetchOptions,
}).unwrap(); }).unwrap();
if (hookOptions.populateCache) {
response.data.groups.forEach((group) => {
dispatch(
prometheusApi.util.upsertQueryData(
'getGroups',
{ ruleSource: { uid: ruleSource.uid }, namespace: group.file, groupName: group.name },
{ data: { groups: [group] }, status: 'success' }
)
);
});
}
return response; return response;
}; };
yield* genericGroupsGenerator(getRuleSourceGroupsWithCache, groupLimit); yield* genericGroupsGenerator(getRuleSourceGroupsWithCache, groupLimit);
}, },
[getGroups, dispatch, hookOptions.populateCache] [getGroups]
); );
} }
@ -67,8 +53,8 @@ interface GrafanaFetchGroupsOptions extends FetchGroupsOptions {
} }
export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions = {}) { export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions = {}) {
const dispatch = useDispatch();
const [getGrafanaGroups] = useLazyGetGrafanaGroupsQuery(); const [getGrafanaGroups] = useLazyGetGrafanaGroupsQuery();
const { populateGroupsResponseCache } = usePopulateGrafanaPrometheusApiCache();
const getGroupsAndProvideCache = useCallback( const getGroupsAndProvideCache = useCallback(
async (fetchOptions: GrafanaFetchGroupsOptions) => { async (fetchOptions: GrafanaFetchGroupsOptions) => {
@ -78,25 +64,13 @@ export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions =
...fetchOptions.filter, ...fetchOptions.filter,
}).unwrap(); }).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
if (hookOptions.populateCache) { if (hookOptions.populateCache) {
const cacheAndRulerPreload = response.data.groups.map(async (group) => { populateGroupsResponseCache(response.data.groups);
await dispatch(
prometheusApi.util.upsertQueryData(
'getGrafanaGroups',
{ folderUid: group.folderUid, groupName: group.name, limitAlerts: hookOptions.limitAlerts },
{ data: { groups: [group] }, status: 'success' }
)
);
});
await Promise.allSettled(cacheAndRulerPreload);
} }
return response; return response;
}, },
[getGrafanaGroups, dispatch, hookOptions.populateCache, hookOptions.limitAlerts] [getGrafanaGroups, hookOptions.limitAlerts, hookOptions.populateCache, populateGroupsResponseCache]
); );
return useCallback( return useCallback(

@ -15,7 +15,6 @@ import {
PromRuleGroupDTO, PromRuleGroupDTO,
} from 'app/types/unified-alerting-dto'; } from 'app/types/unified-alerting-dto';
import { usePopulateGrafanaPrometheusApiCache } from '../../api/prometheusApi';
import { RulesFilter } from '../../search/rulesSearchParser'; import { RulesFilter } from '../../search/rulesSearchParser';
import { import {
getDataSourceByUid, getDataSourceByUid,
@ -52,7 +51,6 @@ interface GetIteratorResult {
} }
export function useFilteredRulesIteratorProvider() { export function useFilteredRulesIteratorProvider() {
const { populateGroupResponseCache } = usePopulateGrafanaPrometheusApiCache();
const allExternalRulesSources = getExternalRulesSources(); const allExternalRulesSources = getExternalRulesSources();
const prometheusGroupsGenerator = usePrometheusGroupsGenerator(); const prometheusGroupsGenerator = usePrometheusGroupsGenerator();
@ -76,10 +74,7 @@ export function useFilteredRulesIteratorProvider() {
concatMap((groups) => concatMap((groups) =>
groups groups
.filter((group) => groupFilter(group, normalizedFilterState)) .filter((group) => groupFilter(group, normalizedFilterState))
.flatMap((group) => { .flatMap((group) => group.rules.map((rule) => [group, rule] as const))
populateGroupResponseCache(group);
return group.rules.map((rule) => [group, rule] as const);
})
.filter(([, rule]) => ruleFilter(rule, normalizedFilterState)) .filter(([, rule]) => ruleFilter(rule, normalizedFilterState))
.map(([group, rule]) => mapGrafanaRuleToRuleWithOrigin(group, rule)) .map(([group, rule]) => mapGrafanaRuleToRuleWithOrigin(group, rule))
), ),

Loading…
Cancel
Save