mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 06:22:35 +08:00

* Basic implementation in web worker * Move instances discovery to the worker * Remove filtering from the worker * Use normalized routes, use rtk query for alert groups fetching * Reorganize matchers utilities to be available for web workers * Move object matchers to the machers util file, rename worker * Move worker code to a separate hook, add perf logging * Add a mock for the web worker code, fix tests * Fix tests warnings * Remove notification policy feature flag * Add normalizeRoute tests, change the regex match to test for label matching * Move worker init to the file scope * Simplify useAsyncFn hook * Use CorsWorker as a workaround for web workers loading from CDN * Use a feature flag to enable/disable worker-based preview, add worker error handling * Add POC for react-enable working with grafana feature toggles * Code cleanup * Remove console error, add useRouteGroupsMatcher tests * Fix tests mock
89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
import * as comlink from 'comlink';
|
|
import { useCallback, useEffect } from 'react';
|
|
import { useEnabled } from 'react-enable';
|
|
|
|
import { logError } from '@grafana/runtime';
|
|
|
|
import { AlertmanagerGroup, RouteWithID } from '../../../plugins/datasource/alertmanager/types';
|
|
|
|
import { logInfo } from './Analytics';
|
|
import { createWorker } from './createRouteGroupsMatcherWorker';
|
|
import { AlertingFeature } from './features';
|
|
import type { RouteGroupsMatcher } from './routeGroupsMatcher.worker';
|
|
|
|
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 };
|
|
}
|
|
|
|
export function useRouteGroupsMatcher() {
|
|
const workerPreviewEnabled = useEnabled(AlertingFeature.NotificationPoliciesV2MatchingInstances);
|
|
|
|
useEffect(() => {
|
|
if (workerPreviewEnabled) {
|
|
const { disposeWorker } = loadWorker();
|
|
return disposeWorker;
|
|
}
|
|
|
|
return () => null;
|
|
}, [workerPreviewEnabled]);
|
|
|
|
const getRouteGroupsMap = useCallback(
|
|
async (rootRoute: RouteWithID, alertGroups: AlertmanagerGroup[]) => {
|
|
if (!workerPreviewEnabled) {
|
|
throw new Error('Matching routes preview is disabled');
|
|
}
|
|
|
|
if (!routeMatcher) {
|
|
throw new Error('Route Matcher has not been initialized');
|
|
}
|
|
|
|
const startTime = performance.now();
|
|
|
|
const result = await routeMatcher.getRouteGroupsMap(rootRoute, alertGroups);
|
|
|
|
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;
|
|
},
|
|
[workerPreviewEnabled]
|
|
);
|
|
|
|
return { getRouteGroupsMap };
|
|
}
|