The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/alerting/unified/useRouteGroupsMatcher.ts

101 lines
3.2 KiB

import * as comlink from 'comlink';
import { useCallback, useEffect } from 'react';
import { AlertmanagerGroup, RouteWithID } from '../../../plugins/datasource/alertmanager/types';
import { Labels } from '../../../types/unified-alerting-dto';
import { logError, logInfo } from './Analytics';
import { createWorker } from './createRouteGroupsMatcherWorker';
import type { MatchOptions, RouteGroupsMatcher } from './routeGroupsMatcher';
let routeMatcher: comlink.Remote<RouteGroupsMatcher> | undefined;
// Load worker loads the worker if it's not loaded yet
// and returns a function to dispose of the worker
// We do it to enable feature toggling. If the feature is disabled we don't wont to load the worker code at all
// An alternative way would be to move all this code to the hook below, but it will create and terminate the worker much more often
function loadWorker() {
let worker: Worker | undefined;
if (routeMatcher === undefined) {
try {
worker = createWorker();
routeMatcher = comlink.wrap<RouteGroupsMatcher>(worker);
} catch (e: unknown) {
if (e instanceof Error) {
logError(e);
}
}
}
const disposeWorker = () => {
if (worker && routeMatcher) {
routeMatcher[comlink.releaseProxy]();
worker.terminate();
routeMatcher = undefined;
worker = undefined;
}
};
return { disposeWorker };
}
function validateWorker(matcher: typeof routeMatcher): asserts matcher is comlink.Remote<RouteGroupsMatcher> {
if (!routeMatcher) {
throw new Error('Route Matcher has not been initialized');
}
}
export function useRouteGroupsMatcher() {
useEffect(() => {
const { disposeWorker } = loadWorker();
return disposeWorker;
}, []);
const getRouteGroupsMap = useCallback(
async (rootRoute: RouteWithID, alertGroups: AlertmanagerGroup[], options?: MatchOptions) => {
validateWorker(routeMatcher);
const startTime = performance.now();
const result = await routeMatcher.getRouteGroupsMap(rootRoute, alertGroups, options);
const timeSpent = performance.now() - startTime;
logInfo(`Route Groups Matched in ${timeSpent} ms`, {
matchingTime: timeSpent.toString(),
alertGroupsCount: alertGroups.length.toString(),
// Counting all nested routes might be too time-consuming, so we only count the first level
topLevelRoutesCount: rootRoute.routes?.length.toString() ?? '0',
});
return result;
},
[]
);
const matchInstancesToRoute = useCallback(
async (rootRoute: RouteWithID, instancesToMatch: Labels[], options?: MatchOptions) => {
validateWorker(routeMatcher);
const startTime = performance.now();
const result = await routeMatcher.matchInstancesToRoute(rootRoute, instancesToMatch, options);
const timeSpent = performance.now() - startTime;
logInfo(`Instances Matched in ${timeSpent} ms`, {
matchingTime: timeSpent.toString(),
instancesToMatchCount: instancesToMatch.length.toString(),
// Counting all nested routes might be too time-consuming, so we only count the first level
topLevelRoutesCount: rootRoute.routes?.length.toString() ?? '0',
});
return result;
},
[]
);
return { getRouteGroupsMap, matchInstancesToRoute };
}