mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Prevent search from locking the browser (#87132)
This commit is contained in:
parent
ad5613d7d4
commit
1d06f33a8e
@ -18,6 +18,10 @@ import { getRuleHealth, isAlertingRule, isGrafanaRulerRule, isPromRuleType } fro
|
||||
import { calculateGroupTotals, calculateRuleFilteredTotals, calculateRuleTotals } from './useCombinedRuleNamespaces';
|
||||
import { useURLSearchParams } from './useURLSearchParams';
|
||||
|
||||
// if the search term is longer than MAX_NEEDLE_SIZE we disable Levenshtein distance
|
||||
const MAX_NEEDLE_SIZE = 25;
|
||||
const INFO_THRESHOLD = Infinity;
|
||||
|
||||
export function useRulesFilter() {
|
||||
const [queryParams, updateQueryParams] = useURLSearchParams();
|
||||
const searchQuery = queryParams.get('search') ?? '';
|
||||
@ -100,17 +104,6 @@ export const useFilteredRules = (namespaces: CombinedRuleNamespace[], filterStat
|
||||
}, [namespaces, filterState]);
|
||||
};
|
||||
|
||||
// Options details can be found here https://github.com/leeoniya/uFuzzy#options
|
||||
// The following configuration complies with Damerau-Levenshtein distance
|
||||
// https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
|
||||
const ufuzzy = new uFuzzy({
|
||||
intraMode: 1,
|
||||
intraIns: 1,
|
||||
intraSub: 1,
|
||||
intraTrn: 1,
|
||||
intraDel: 1,
|
||||
});
|
||||
|
||||
export const filterRules = (
|
||||
namespaces: CombinedRuleNamespace[],
|
||||
filterState: RulesFilter = { dataSourceNames: [], labels: [], freeFormWords: [] }
|
||||
@ -129,7 +122,13 @@ export const filterRules = (
|
||||
if (namespaceFilter) {
|
||||
const namespaceHaystack = filteredNamespaces.map((ns) => ns.name);
|
||||
|
||||
const [idxs, info, order] = ufuzzy.search(namespaceHaystack, namespaceFilter);
|
||||
const ufuzzy = getSearchInstance(namespaceFilter);
|
||||
const [idxs, info, order] = ufuzzy.search(
|
||||
namespaceHaystack,
|
||||
namespaceFilter,
|
||||
getOutOfOrderLimit(namespaceFilter),
|
||||
INFO_THRESHOLD
|
||||
);
|
||||
if (info && order) {
|
||||
filteredNamespaces = order.map((idx) => filteredNamespaces[info.idx[idx]]);
|
||||
} else if (idxs) {
|
||||
@ -148,7 +147,14 @@ const reduceNamespaces = (filterState: RulesFilter) => {
|
||||
|
||||
if (groupNameFilter) {
|
||||
const groupsHaystack = filteredGroups.map((g) => g.name);
|
||||
const [idxs, info, order] = ufuzzy.search(groupsHaystack, groupNameFilter);
|
||||
const ufuzzy = getSearchInstance(groupNameFilter);
|
||||
|
||||
const [idxs, info, order] = ufuzzy.search(
|
||||
groupsHaystack,
|
||||
groupNameFilter,
|
||||
getOutOfOrderLimit(groupNameFilter),
|
||||
INFO_THRESHOLD
|
||||
);
|
||||
if (info && order) {
|
||||
filteredGroups = order.map((idx) => filteredGroups[info.idx[idx]]);
|
||||
} else if (idxs) {
|
||||
@ -178,7 +184,14 @@ const reduceGroups = (filterState: RulesFilter) => {
|
||||
|
||||
if (ruleNameQuery) {
|
||||
const rulesHaystack = filteredRules.map((r) => r.name);
|
||||
const [idxs, info, order] = ufuzzy.search(rulesHaystack, ruleNameQuery);
|
||||
const ufuzzy = getSearchInstance(ruleNameQuery);
|
||||
|
||||
const [idxs, info, order] = ufuzzy.search(
|
||||
rulesHaystack,
|
||||
ruleNameQuery,
|
||||
getOutOfOrderLimit(ruleNameQuery),
|
||||
INFO_THRESHOLD
|
||||
);
|
||||
if (info && order) {
|
||||
filteredRules = order.map((idx) => filteredRules[info.idx[idx]]);
|
||||
} else if (idxs) {
|
||||
@ -275,6 +288,15 @@ const reduceGroups = (filterState: RulesFilter) => {
|
||||
};
|
||||
};
|
||||
|
||||
// apply an outOfOrder limit which helps to limit the number of permutations to search for
|
||||
// and prevents the browser from hanging
|
||||
function getOutOfOrderLimit(searchTerm: string) {
|
||||
const ufuzzy = getSearchInstance(searchTerm);
|
||||
|
||||
const termCount = ufuzzy.split(searchTerm).length;
|
||||
return termCount < 5 ? 4 : 0;
|
||||
}
|
||||
|
||||
function looseParseMatcher(matcherQuery: string): Matcher | undefined {
|
||||
try {
|
||||
return parseMatcher(matcherQuery);
|
||||
@ -284,6 +306,23 @@ function looseParseMatcher(matcherQuery: string): Matcher | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
// determine which search instance to use, very long search terms should match without checking for Levenshtein distance
|
||||
function getSearchInstance(searchTerm: string): uFuzzy {
|
||||
const searchTermExeedsMaxNeedleSize = searchTerm.length > MAX_NEEDLE_SIZE;
|
||||
|
||||
// Options details can be found here https://github.com/leeoniya/uFuzzy#options
|
||||
// The following configuration complies with Damerau-Levenshtein distance
|
||||
// https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
|
||||
return new uFuzzy({
|
||||
// we will disable Levenshtein distance for very long search terms – this will help with performance
|
||||
// as it will avoid creating a very complex regular expression
|
||||
intraMode: searchTermExeedsMaxNeedleSize ? 0 : 1,
|
||||
// split search terms only on whitespace, this will significantly reduce the amount of regex permutations to test
|
||||
// and is important for performance with large amount of rules and large needle
|
||||
interSplit: '\\s+',
|
||||
});
|
||||
}
|
||||
|
||||
const isQueryingDataSource = (rulerRule: RulerGrafanaRuleDTO, filterState: RulesFilter): boolean => {
|
||||
if (!filterState.dataSourceNames?.length) {
|
||||
return true;
|
||||
|
Loading…
Reference in New Issue
Block a user