Alerting: Prevent search from locking the browser (#87132)
This commit is contained in:
@@ -18,6 +18,10 @@ import { getRuleHealth, isAlertingRule, isGrafanaRulerRule, isPromRuleType } fro
|
|||||||
import { calculateGroupTotals, calculateRuleFilteredTotals, calculateRuleTotals } from './useCombinedRuleNamespaces';
|
import { calculateGroupTotals, calculateRuleFilteredTotals, calculateRuleTotals } from './useCombinedRuleNamespaces';
|
||||||
import { useURLSearchParams } from './useURLSearchParams';
|
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() {
|
export function useRulesFilter() {
|
||||||
const [queryParams, updateQueryParams] = useURLSearchParams();
|
const [queryParams, updateQueryParams] = useURLSearchParams();
|
||||||
const searchQuery = queryParams.get('search') ?? '';
|
const searchQuery = queryParams.get('search') ?? '';
|
||||||
@@ -100,17 +104,6 @@ export const useFilteredRules = (namespaces: CombinedRuleNamespace[], filterStat
|
|||||||
}, [namespaces, filterState]);
|
}, [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 = (
|
export const filterRules = (
|
||||||
namespaces: CombinedRuleNamespace[],
|
namespaces: CombinedRuleNamespace[],
|
||||||
filterState: RulesFilter = { dataSourceNames: [], labels: [], freeFormWords: [] }
|
filterState: RulesFilter = { dataSourceNames: [], labels: [], freeFormWords: [] }
|
||||||
@@ -129,7 +122,13 @@ export const filterRules = (
|
|||||||
if (namespaceFilter) {
|
if (namespaceFilter) {
|
||||||
const namespaceHaystack = filteredNamespaces.map((ns) => ns.name);
|
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) {
|
if (info && order) {
|
||||||
filteredNamespaces = order.map((idx) => filteredNamespaces[info.idx[idx]]);
|
filteredNamespaces = order.map((idx) => filteredNamespaces[info.idx[idx]]);
|
||||||
} else if (idxs) {
|
} else if (idxs) {
|
||||||
@@ -148,7 +147,14 @@ const reduceNamespaces = (filterState: RulesFilter) => {
|
|||||||
|
|
||||||
if (groupNameFilter) {
|
if (groupNameFilter) {
|
||||||
const groupsHaystack = filteredGroups.map((g) => g.name);
|
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) {
|
if (info && order) {
|
||||||
filteredGroups = order.map((idx) => filteredGroups[info.idx[idx]]);
|
filteredGroups = order.map((idx) => filteredGroups[info.idx[idx]]);
|
||||||
} else if (idxs) {
|
} else if (idxs) {
|
||||||
@@ -178,7 +184,14 @@ const reduceGroups = (filterState: RulesFilter) => {
|
|||||||
|
|
||||||
if (ruleNameQuery) {
|
if (ruleNameQuery) {
|
||||||
const rulesHaystack = filteredRules.map((r) => r.name);
|
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) {
|
if (info && order) {
|
||||||
filteredRules = order.map((idx) => filteredRules[info.idx[idx]]);
|
filteredRules = order.map((idx) => filteredRules[info.idx[idx]]);
|
||||||
} else if (idxs) {
|
} 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 {
|
function looseParseMatcher(matcherQuery: string): Matcher | undefined {
|
||||||
try {
|
try {
|
||||||
return parseMatcher(matcherQuery);
|
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 => {
|
const isQueryingDataSource = (rulerRule: RulerGrafanaRuleDTO, filterState: RulesFilter): boolean => {
|
||||||
if (!filterState.dataSourceNames?.length) {
|
if (!filterState.dataSourceNames?.length) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user