mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 00:39:13 +08:00
102 lines
3.2 KiB
TypeScript
102 lines
3.2 KiB
TypeScript
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 };
|
|
}
|