mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Reduce number of unnecessary request in the alert list panel in case … (#70583)
* Reduce number of unnecessary request in the alert list panel in case we have data source filter selected * use fetchPromAndRulerRulesAction instead of fetchAllPromAndRulerRulesAction in case of having datasource selected in options * remove unnecessary useEffect * Reduce number of requests in GroupBy component if data source is selected * use redux utils for asyncmapslice * lint * Address review comments * Skip fetching grafana rules if datasource is not Grafana rules source name --------- Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
This commit is contained in:
parent
e2dd1e9055
commit
97420119e1
@ -246,15 +246,23 @@ export const fetchRulerRulesAction = createAsyncThunk(
|
|||||||
export function fetchPromAndRulerRulesAction({
|
export function fetchPromAndRulerRulesAction({
|
||||||
rulesSourceName,
|
rulesSourceName,
|
||||||
identifier,
|
identifier,
|
||||||
|
filter,
|
||||||
|
limitAlerts,
|
||||||
|
matcher,
|
||||||
|
state,
|
||||||
}: {
|
}: {
|
||||||
rulesSourceName: string;
|
rulesSourceName: string;
|
||||||
identifier?: RuleIdentifier;
|
identifier?: RuleIdentifier;
|
||||||
|
filter?: FetchPromRulesFilter;
|
||||||
|
limitAlerts?: number;
|
||||||
|
matcher?: Matcher[];
|
||||||
|
state?: string[];
|
||||||
}): ThunkResult<void> {
|
}): ThunkResult<void> {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
await dispatch(fetchRulesSourceBuildInfoAction({ rulesSourceName }));
|
await dispatch(fetchRulesSourceBuildInfoAction({ rulesSourceName }));
|
||||||
const dsConfig = getDataSourceConfig(getState, rulesSourceName);
|
const dsConfig = getDataSourceConfig(getState, rulesSourceName);
|
||||||
|
|
||||||
await dispatch(fetchPromRulesAction({ rulesSourceName, identifier }));
|
await dispatch(fetchPromRulesAction({ rulesSourceName, identifier, filter, limitAlerts, matcher, state }));
|
||||||
if (dsConfig.rulerConfig) {
|
if (dsConfig.rulerConfig) {
|
||||||
await dispatch(fetchRulerRulesAction({ rulesSourceName }));
|
await dispatch(fetchRulerRulesAction({ rulesSourceName }));
|
||||||
}
|
}
|
||||||
|
@ -188,6 +188,14 @@ export function isAsyncRequestMapSlicePending<T>(slice: AsyncRequestMapSlice<T>)
|
|||||||
return Object.values(slice).some(isAsyncRequestStatePending);
|
return Object.values(slice).some(isAsyncRequestStatePending);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isAsyncRequestMapSlicePartiallyDispatched<T>(slice: AsyncRequestMapSlice<T>): boolean {
|
||||||
|
return Object.values(slice).some((state) => state.dispatched);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAsyncRequestMapSlicePartiallyFulfilled<T>(slice: AsyncRequestMapSlice<T>): boolean {
|
||||||
|
return Object.values(slice).some(isAsyncRequestStateFulfilled);
|
||||||
|
}
|
||||||
|
|
||||||
export function isAsyncRequestStatePending<T>(state?: AsyncRequestState<T>): boolean {
|
export function isAsyncRequestStatePending<T>(state?: AsyncRequestState<T>): boolean {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -13,21 +13,28 @@ import { useDispatch } from 'app/types';
|
|||||||
import { AlertingRule } from 'app/types/unified-alerting';
|
import { AlertingRule } from 'app/types/unified-alerting';
|
||||||
import { PromRuleType } from 'app/types/unified-alerting-dto';
|
import { PromRuleType } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
|
import { fetchPromRulesAction } from '../../../features/alerting/unified/state/actions';
|
||||||
|
|
||||||
import { isPrivateLabel } from './util';
|
import { isPrivateLabel } from './util';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string;
|
id: string;
|
||||||
defaultValue: SelectableValue<string>;
|
defaultValue: SelectableValue<string>;
|
||||||
onChange: (keys: string[]) => void;
|
onChange: (keys: string[]) => void;
|
||||||
|
dataSource?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GroupBy = (props: Props) => {
|
export const GroupBy = (props: Props) => {
|
||||||
const { onChange, id, defaultValue } = props;
|
const { onChange, id, defaultValue, dataSource } = props;
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchAllPromRulesAction());
|
if (dataSource) {
|
||||||
}, [dispatch]);
|
dataSource && dispatch(fetchPromRulesAction({ rulesSourceName: dataSource }));
|
||||||
|
} else {
|
||||||
|
dispatch(fetchAllPromRulesAction());
|
||||||
|
}
|
||||||
|
}, [dispatch, dataSource]);
|
||||||
|
|
||||||
const promRulesByDatasource = useUnifiedAlertingSelector((state) => state.promRules);
|
const promRulesByDatasource = useUnifiedAlertingSelector((state) => state.promRules);
|
||||||
|
|
||||||
|
@ -22,19 +22,23 @@ import { alertRuleApi } from 'app/features/alerting/unified/api/alertRuleApi';
|
|||||||
import { INSTANCES_DISPLAY_LIMIT } from 'app/features/alerting/unified/components/rules/RuleDetails';
|
import { INSTANCES_DISPLAY_LIMIT } from 'app/features/alerting/unified/components/rules/RuleDetails';
|
||||||
import { useCombinedRuleNamespaces } from 'app/features/alerting/unified/hooks/useCombinedRuleNamespaces';
|
import { useCombinedRuleNamespaces } from 'app/features/alerting/unified/hooks/useCombinedRuleNamespaces';
|
||||||
import { useUnifiedAlertingSelector } from 'app/features/alerting/unified/hooks/useUnifiedAlertingSelector';
|
import { useUnifiedAlertingSelector } from 'app/features/alerting/unified/hooks/useUnifiedAlertingSelector';
|
||||||
import { fetchAllPromAndRulerRulesAction } from 'app/features/alerting/unified/state/actions';
|
import {
|
||||||
|
fetchAllPromAndRulerRulesAction,
|
||||||
|
fetchPromAndRulerRulesAction,
|
||||||
|
} from 'app/features/alerting/unified/state/actions';
|
||||||
import { parseMatchers } from 'app/features/alerting/unified/utils/alertmanager';
|
import { parseMatchers } from 'app/features/alerting/unified/utils/alertmanager';
|
||||||
import { Annotation } from 'app/features/alerting/unified/utils/constants';
|
import { Annotation } from 'app/features/alerting/unified/utils/constants';
|
||||||
|
import { GRAFANA_DATASOURCE_NAME, GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
|
||||||
import {
|
import {
|
||||||
getAllRulesSourceNames,
|
isAsyncRequestMapSlicePartiallyDispatched,
|
||||||
GRAFANA_DATASOURCE_NAME,
|
isAsyncRequestMapSlicePartiallyFulfilled,
|
||||||
GRAFANA_RULES_SOURCE_NAME,
|
isAsyncRequestMapSlicePending,
|
||||||
} from 'app/features/alerting/unified/utils/datasource';
|
} from 'app/features/alerting/unified/utils/redux';
|
||||||
import { initialAsyncRequestState } from 'app/features/alerting/unified/utils/redux';
|
|
||||||
import { flattenCombinedRules, getFirstActiveAt } from 'app/features/alerting/unified/utils/rules';
|
import { flattenCombinedRules, getFirstActiveAt } from 'app/features/alerting/unified/utils/rules';
|
||||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
import { AccessControlAction, useDispatch } from 'app/types';
|
import { Matcher } from 'app/plugins/datasource/alertmanager/types';
|
||||||
|
import { AccessControlAction, ThunkDispatch, useDispatch } from 'app/types';
|
||||||
import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
|
import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
import { getAlertingRule } from '../../../features/alerting/unified/utils/rules';
|
import { getAlertingRule } from '../../../features/alerting/unified/utils/rules';
|
||||||
@ -56,13 +60,50 @@ function getStateList(state: StateFilter) {
|
|||||||
return Object.entries(state).reduce(reducer, []);
|
return Object.entries(state).reduce(reducer, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchPromAndRuler = ({
|
||||||
|
dispatch,
|
||||||
|
limitInstances,
|
||||||
|
matcherList,
|
||||||
|
dataSourceName,
|
||||||
|
stateList,
|
||||||
|
}: {
|
||||||
|
dispatch: ThunkDispatch;
|
||||||
|
limitInstances: boolean;
|
||||||
|
matcherList?: Matcher[] | undefined;
|
||||||
|
dataSourceName?: string;
|
||||||
|
stateList: string[];
|
||||||
|
}) => {
|
||||||
|
if (dataSourceName) {
|
||||||
|
dispatch(
|
||||||
|
fetchPromAndRulerRulesAction({
|
||||||
|
rulesSourceName: dataSourceName,
|
||||||
|
limitAlerts: limitInstances ? INSTANCES_DISPLAY_LIMIT : undefined,
|
||||||
|
matcher: matcherList,
|
||||||
|
state: stateList,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
dispatch(
|
||||||
|
fetchAllPromAndRulerRulesAction(false, {
|
||||||
|
limitAlerts: limitInstances ? INSTANCES_DISPLAY_LIMIT : undefined,
|
||||||
|
matcher: matcherList,
|
||||||
|
state: stateList,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const rulesDataSourceNames = useMemo(getAllRulesSourceNames, []);
|
|
||||||
const [limitInstances, toggleLimit] = useToggle(true);
|
const [limitInstances, toggleLimit] = useToggle(true);
|
||||||
|
|
||||||
const { usePrometheusRulesByNamespaceQuery } = alertRuleApi;
|
const { usePrometheusRulesByNamespaceQuery } = alertRuleApi;
|
||||||
|
|
||||||
|
const promRulesRequests = useUnifiedAlertingSelector((state) => state.promRules);
|
||||||
|
const rulerRulesRequests = useUnifiedAlertingSelector((state) => state.rulerRules);
|
||||||
|
|
||||||
|
const somePromRulesDispatched = isAsyncRequestMapSlicePartiallyDispatched(promRulesRequests);
|
||||||
|
|
||||||
// backwards compat for "Inactive" state filter
|
// backwards compat for "Inactive" state filter
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.options.stateFilter.inactive === true) {
|
if (props.options.stateFilter.inactive === true) {
|
||||||
@ -79,6 +120,8 @@ export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
|||||||
|
|
||||||
const stateList = useMemo(() => getStateList(props.options.stateFilter), [props.options.stateFilter]);
|
const stateList = useMemo(() => getStateList(props.options.stateFilter), [props.options.stateFilter]);
|
||||||
const { options, replaceVariables } = props;
|
const { options, replaceVariables } = props;
|
||||||
|
const dataSourceName =
|
||||||
|
options.datasource === GRAFANA_DATASOURCE_NAME ? GRAFANA_RULES_SOURCE_NAME : options.datasource;
|
||||||
const parsedOptions: UnifiedAlertListOptions = {
|
const parsedOptions: UnifiedAlertListOptions = {
|
||||||
...props.options,
|
...props.options,
|
||||||
alertName: replaceVariables(options.alertName),
|
alertName: replaceVariables(options.alertName),
|
||||||
@ -90,87 +133,47 @@ export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
|||||||
[parsedOptions.alertInstanceLabelFilter]
|
[parsedOptions.alertInstanceLabelFilter]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.options.groupMode === GroupMode.Default) {
|
|
||||||
dispatch(
|
|
||||||
fetchAllPromAndRulerRulesAction(false, {
|
|
||||||
limitAlerts: limitInstances ? INSTANCES_DISPLAY_LIMIT : undefined,
|
|
||||||
matcher: matcherList,
|
|
||||||
state: stateList,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [props.options.groupMode, limitInstances, dispatch, matcherList, stateList]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//we need promRules and rulerRules for getting the uid when creating the alert link in panel in case of being a rulerRule.
|
//we need promRules and rulerRules for getting the uid when creating the alert link in panel in case of being a rulerRule.
|
||||||
dispatch(
|
if (!promRulesRequests.loading) {
|
||||||
fetchAllPromAndRulerRulesAction(false, {
|
fetchPromAndRuler({ dispatch, limitInstances, matcherList, dataSourceName, stateList });
|
||||||
limitAlerts: limitInstances ? INSTANCES_DISPLAY_LIMIT : undefined,
|
}
|
||||||
matcher: matcherList,
|
|
||||||
state: stateList,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const sub = dashboard?.events.subscribe(TimeRangeUpdatedEvent, () =>
|
const sub = dashboard?.events.subscribe(TimeRangeUpdatedEvent, () =>
|
||||||
dispatch(
|
fetchPromAndRuler({ dispatch, limitInstances, matcherList, dataSourceName, stateList })
|
||||||
fetchAllPromAndRulerRulesAction(false, {
|
|
||||||
limitAlerts: limitInstances ? INSTANCES_DISPLAY_LIMIT : undefined,
|
|
||||||
matcher: matcherList,
|
|
||||||
state: stateList,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
return () => {
|
return () => {
|
||||||
sub?.unsubscribe();
|
sub?.unsubscribe();
|
||||||
};
|
};
|
||||||
}, [dispatch, dashboard, matcherList, stateList, toggleLimit, limitInstances]);
|
}, [dispatch, dashboard, matcherList, stateList, limitInstances, dataSourceName, promRulesRequests.loading]);
|
||||||
|
|
||||||
const handleInstancesLimit = (limit: boolean) => {
|
const handleInstancesLimit = (limit: boolean) => {
|
||||||
if (limit) {
|
if (limit) {
|
||||||
dispatch(
|
fetchPromAndRuler({ dispatch, limitInstances, matcherList, dataSourceName, stateList });
|
||||||
fetchAllPromAndRulerRulesAction(false, {
|
|
||||||
limitAlerts: INSTANCES_DISPLAY_LIMIT,
|
|
||||||
matcher: matcherList,
|
|
||||||
state: stateList,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
toggleLimit(true);
|
toggleLimit(true);
|
||||||
} else {
|
} else {
|
||||||
dispatch(
|
fetchPromAndRuler({ dispatch, limitInstances: false, matcherList, dataSourceName, stateList });
|
||||||
fetchAllPromAndRulerRulesAction(false, {
|
|
||||||
matcher: matcherList,
|
|
||||||
state: stateList,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
toggleLimit(false);
|
toggleLimit(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { prom, ruler } = useUnifiedAlertingSelector((state) => ({
|
|
||||||
prom: state.promRules[GRAFANA_RULES_SOURCE_NAME] || initialAsyncRequestState,
|
|
||||||
ruler: state.rulerRules[GRAFANA_RULES_SOURCE_NAME] || initialAsyncRequestState,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const loading = prom.loading || ruler.loading;
|
|
||||||
const haveResults = !!prom.result || !!ruler.result;
|
|
||||||
|
|
||||||
const promRulesRequests = useUnifiedAlertingSelector((state) => state.promRules);
|
|
||||||
const rulerRulesRequests = useUnifiedAlertingSelector((state) => state.rulerRules);
|
|
||||||
|
|
||||||
const somePromRulesDispatched = rulesDataSourceNames.some((name) => promRulesRequests[name]?.dispatched);
|
|
||||||
|
|
||||||
//For grafana managed rules, get the result using RTK Query to avoid the need of using the redux store
|
//For grafana managed rules, get the result using RTK Query to avoid the need of using the redux store
|
||||||
//See https://github.com/grafana/grafana/pull/70482
|
//See https://github.com/grafana/grafana/pull/70482
|
||||||
const { currentData: promRules = [], isLoading: grafanaRulesLoading } = usePrometheusRulesByNamespaceQuery({
|
const { currentData: promRules = [], isLoading: grafanaRulesLoading } = usePrometheusRulesByNamespaceQuery(
|
||||||
limitAlerts: limitInstances ? INSTANCES_DISPLAY_LIMIT : undefined,
|
{
|
||||||
matcher: matcherList,
|
limitAlerts: limitInstances ? INSTANCES_DISPLAY_LIMIT : undefined,
|
||||||
state: stateList,
|
matcher: matcherList,
|
||||||
});
|
state: stateList,
|
||||||
|
},
|
||||||
|
{ skip: dataSourceName !== GRAFANA_RULES_SOURCE_NAME }
|
||||||
|
);
|
||||||
|
|
||||||
const combinedRules = useCombinedRuleNamespaces(undefined, promRules);
|
const combinedRules = useCombinedRuleNamespaces(undefined, promRules);
|
||||||
|
|
||||||
const someRulerRulesDispatched = rulesDataSourceNames.some((name) => rulerRulesRequests[name]?.dispatched);
|
const someRulerRulesDispatched = isAsyncRequestMapSlicePartiallyDispatched(rulerRulesRequests);
|
||||||
|
const haveResults = isAsyncRequestMapSlicePartiallyFulfilled(promRulesRequests);
|
||||||
|
|
||||||
const dispatched = somePromRulesDispatched || someRulerRulesDispatched;
|
const dispatched = somePromRulesDispatched || someRulerRulesDispatched;
|
||||||
|
const loading = isAsyncRequestMapSlicePending(promRulesRequests);
|
||||||
|
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ const defaultOptions: UnifiedAlertListOptions = {
|
|||||||
folder: { id: 1, title: 'test folder' },
|
folder: { id: 1, title: 'test folder' },
|
||||||
stateFilter: { firing: true, pending: false, noData: false, normal: true, error: false },
|
stateFilter: { firing: true, pending: false, noData: false, normal: true, error: false },
|
||||||
alertInstanceLabelFilter: '',
|
alertInstanceLabelFilter: '',
|
||||||
datasource: 'Alertmanager',
|
datasource: 'grafana',
|
||||||
viewMode: ViewMode.List,
|
viewMode: ViewMode.List,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -197,6 +197,7 @@ const unifiedAlertList = new PanelPlugin<UnifiedAlertListOptions>(UnifiedAlertLi
|
|||||||
id={props.id ?? 'groupBy'}
|
id={props.id ?? 'groupBy'}
|
||||||
defaultValue={props.value.map((value: string) => ({ label: value, value }))}
|
defaultValue={props.value.map((value: string) => ({ label: value, value }))}
|
||||||
onChange={props.onChange}
|
onChange={props.onChange}
|
||||||
|
dataSource={props.context.options.datasource}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user