From f138440e06d6e1efadc8f294dcf1f4276813061a Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Fri, 8 Nov 2024 14:14:41 +0100 Subject: [PATCH] [ML] AIOps: Optimize initial load bundles. (#198786) ## Summary Part of https://github.com/elastic/kibana/issues/194171. The AIOps plugin loads quite some bundle chunks on the first Kibana load. On main it currently looks like this on page load: ![CleanShot 2024-11-04 at 14 56 52@2x](https://github.com/user-attachments/assets/cbabeaa2-848c-4970-aea5-b06befed0bec) This means while the `aiops.plugin.js` bundle has just 10KB, we load 290KB in total via async bundles on every Kibana full page load. This PR refactors how embeddables and UI actions get initialized to avoid loading any additional async bundles on page load. This increases `aiops.plugin.js` to ~15KB, but gets rid of all the rest! ![CleanShot 2024-11-04 at 15 02 10@2x](https://github.com/user-attachments/assets/557e240f-2c1c-434d-a936-dddb11ab68b6) ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels) (cherry picked from commit 33c303df2b8257546507b60d847c235920e70e48) --- packages/kbn-optimizer/limits.yml | 2 +- .../categorize_field_actions.ts | 2 +- .../components/log_categorization/index.ts | 1 - .../log_categorization/show_flyout.tsx | 2 +- .../embeddable_change_point_chart_factory.tsx | 15 +++++----- .../embeddable_pattern_analysis_factory.tsx | 15 +++++----- x-pack/plugins/aiops/public/plugin.tsx | 28 +++++-------------- .../aiops/public/shared_lazy_components.tsx | 20 +++++++++++++ .../ui_actions/change_point_action_context.ts | 3 +- .../plugins/aiops/public/ui_actions/index.ts | 2 +- .../ui_actions/open_change_point_ml.tsx | 13 +++++---- 11 files changed, 57 insertions(+), 46 deletions(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 7936e52ccbf1..9e7059de1113 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -2,7 +2,7 @@ pageLoadAssetSize: actions: 20000 advancedSettings: 27596 aiAssistantManagementSelection: 19146 - aiops: 10000 + aiops: 16000 alerting: 106936 apm: 64385 banners: 17946 diff --git a/x-pack/plugins/aiops/public/components/log_categorization/categorize_field_actions.ts b/x-pack/plugins/aiops/public/components/log_categorization/categorize_field_actions.ts index 10c6311d065d..e5e6ede86355 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/categorize_field_actions.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/categorize_field_actions.ts @@ -10,7 +10,6 @@ import { createAction } from '@kbn/ui-actions-plugin/public'; import type { CoreStart } from '@kbn/core/public'; import { ACTION_CATEGORIZE_FIELD, type CategorizeFieldContext } from '@kbn/ml-ui-actions'; import type { AiopsPluginStartDeps } from '../../types'; -import { showCategorizeFlyout } from './show_flyout'; export const createCategorizeFieldAction = (coreStart: CoreStart, plugins: AiopsPluginStartDeps) => createAction({ @@ -25,6 +24,7 @@ export const createCategorizeFieldAction = (coreStart: CoreStart, plugins: Aiops }, execute: async (context: CategorizeFieldContext) => { const { field, dataView, originatingApp, additionalFilter } = context; + const { showCategorizeFlyout } = await import('./show_flyout'); showCategorizeFlyout(field, dataView, coreStart, plugins, originatingApp, additionalFilter); }, }); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/index.ts b/x-pack/plugins/aiops/public/components/log_categorization/index.ts index ace01d4f0338..748a0f848642 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/index.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/index.ts @@ -7,7 +7,6 @@ export type { LogCategorizationAppStateProps } from './log_categorization_app_state'; import { LogCategorizationAppState } from './log_categorization_app_state'; -export { createCategorizeFieldAction } from './categorize_field_actions'; // required for dynamic import using React.lazy() // eslint-disable-next-line import/no-default-export diff --git a/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx b/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx index a97f4c7f7fe7..6dae21c36222 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx +++ b/x-pack/plugins/aiops/public/components/log_categorization/show_flyout.tsx @@ -36,7 +36,7 @@ export async function showCategorizeFlyout( ): Promise { const { overlays, application, i18n } = coreStart; - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { try { const onFlyoutClose = () => { flyoutSession.close(); diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx index 2ce1a46780db..5f7ff6ff67f7 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_change_point_chart_factory.tsx @@ -15,13 +15,6 @@ import type { DataView } from '@kbn/data-views-plugin/common'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; -import { - apiHasExecutionContext, - fetch$, - initializeTimeRange, - initializeTitles, - useBatchedPublishingSubjects, -} from '@kbn/presentation-publishing'; import fastIsEqual from 'fast-deep-equal'; import { cloneDeep } from 'lodash'; @@ -61,6 +54,14 @@ export const getChangePointChartEmbeddableFactory = ( return serializedState; }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { + const { + apiHasExecutionContext, + fetch$, + initializeTimeRange, + initializeTitles, + useBatchedPublishingSubjects, + } = await import('@kbn/presentation-publishing'); + const [coreStart, pluginStart] = await getStartServices(); const { diff --git a/x-pack/plugins/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx b/x-pack/plugins/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx index d84043ea5f63..e0017668b338 100644 --- a/x-pack/plugins/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx +++ b/x-pack/plugins/aiops/public/embeddables/pattern_analysis/embeddable_pattern_analysis_factory.tsx @@ -15,13 +15,6 @@ import type { DataView } from '@kbn/data-views-plugin/common'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; -import { - apiHasExecutionContext, - fetch$, - initializeTimeRange, - initializeTitles, - useBatchedPublishingSubjects, -} from '@kbn/presentation-publishing'; import fastIsEqual from 'fast-deep-equal'; import { cloneDeep } from 'lodash'; import React, { useMemo } from 'react'; @@ -58,6 +51,14 @@ export const getPatternAnalysisEmbeddableFactory = ( return serializedState; }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { + const { + apiHasExecutionContext, + fetch$, + initializeTimeRange, + initializeTitles, + useBatchedPublishingSubjects, + } = await import('@kbn/presentation-publishing'); + const [coreStart, pluginStart] = await getStartServices(); const { diff --git a/x-pack/plugins/aiops/public/plugin.tsx b/x-pack/plugins/aiops/public/plugin.tsx index ceb378b0f29b..5863ea03b307 100755 --- a/x-pack/plugins/aiops/public/plugin.tsx +++ b/x-pack/plugins/aiops/public/plugin.tsx @@ -8,15 +8,18 @@ import type { CoreStart, Plugin } from '@kbn/core/public'; import { type CoreSetup } from '@kbn/core/public'; import { firstValueFrom } from 'rxjs'; -import { dynamic } from '@kbn/shared-ux-utility'; import { getChangePointDetectionComponent } from './shared_components'; +import { LogCategorizationForDiscover as PatternAnalysisComponent } from './shared_lazy_components'; import type { AiopsPluginSetup, AiopsPluginSetupDeps, AiopsPluginStart, AiopsPluginStartDeps, } from './types'; +import { registerEmbeddables } from './embeddables'; +import { registerAiopsUiActions } from './ui_actions'; +import { registerChangePointChartsAttachment } from './cases/register_change_point_charts_attachment'; export type AiopsCoreSetup = CoreSetup; @@ -27,20 +30,8 @@ export class AiopsPlugin core: AiopsCoreSetup, { embeddable, cases, licensing, uiActions }: AiopsPluginSetupDeps ) { - Promise.all([ - firstValueFrom(licensing.license$), - import('./embeddables'), - import('./ui_actions'), - import('./cases/register_change_point_charts_attachment'), - core.getStartServices(), - ]).then( - ([ - license, - { registerEmbeddables }, - { registerAiopsUiActions }, - { registerChangePointChartsAttachment }, - [coreStart, pluginStart], - ]) => { + Promise.all([firstValueFrom(licensing.license$), core.getStartServices()]).then( + ([license, [coreStart, pluginStart]]) => { const { canUseAiops } = coreStart.application.capabilities.ml; if (license.hasAtLeast('platinum') && canUseAiops) { @@ -69,12 +60,7 @@ export class AiopsPlugin ); return getPatternAnalysisAvailable(plugins.licensing); }, - PatternAnalysisComponent: dynamic( - async () => - import( - './components/log_categorization/log_categorization_for_embeddable/log_categorization_for_discover_wrapper' - ) - ), + PatternAnalysisComponent, }; } diff --git a/x-pack/plugins/aiops/public/shared_lazy_components.tsx b/x-pack/plugins/aiops/public/shared_lazy_components.tsx index fe9e31f14659..b34efdd6bff0 100644 --- a/x-pack/plugins/aiops/public/shared_lazy_components.tsx +++ b/x-pack/plugins/aiops/public/shared_lazy_components.tsx @@ -12,6 +12,7 @@ import { EuiErrorBoundary, EuiSkeletonText } from '@elastic/eui'; import type { LogRateAnalysisAppStateProps } from './components/log_rate_analysis'; import type { LogRateAnalysisContentWrapperProps } from './components/log_rate_analysis/log_rate_analysis_content/log_rate_analysis_content_wrapper'; import type { LogCategorizationAppStateProps } from './components/log_categorization'; +import type { LogCategorizationEmbeddableWrapperProps } from './components/log_categorization/log_categorization_for_embeddable/log_categorization_for_discover_wrapper'; import type { ChangePointDetectionAppStateProps } from './components/change_point_detection'; const LogRateAnalysisAppStateLazy = React.lazy(() => import('./components/log_rate_analysis')); @@ -58,6 +59,25 @@ export const LogCategorization: FC = (props) => ); +const LogCategorizationForDiscoverLazy = React.lazy( + () => + import( + './components/log_categorization/log_categorization_for_embeddable/log_categorization_for_discover_wrapper' + ) +); + +/** + * Lazy-wrapped LogCategorizationForDiscover React component + * @param {LogCategorizationEmbeddableWrapperProps} props - properties specifying the data on which to run the analysis. + */ +export const LogCategorizationForDiscover: FC = ( + props +) => ( + + + +); + const ChangePointDetectionLazy = React.lazy(() => import('./components/change_point_detection')); /** * Lazy-wrapped ChangePointDetectionAppStateProps React component diff --git a/x-pack/plugins/aiops/public/ui_actions/change_point_action_context.ts b/x-pack/plugins/aiops/public/ui_actions/change_point_action_context.ts index a4307b69d3fa..3bf2eee92256 100644 --- a/x-pack/plugins/aiops/public/ui_actions/change_point_action_context.ts +++ b/x-pack/plugins/aiops/public/ui_actions/change_point_action_context.ts @@ -6,7 +6,8 @@ */ import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { apiIsOfType, type EmbeddableApiContext } from '@kbn/presentation-publishing'; +import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import { apiIsOfType } from '@kbn/presentation-publishing/interfaces/has_type'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '@kbn/aiops-change-point-detection/constants'; import type { ChangePointEmbeddableApi } from '../embeddables/change_point_chart/types'; diff --git a/x-pack/plugins/aiops/public/ui_actions/index.ts b/x-pack/plugins/aiops/public/ui_actions/index.ts index 6081541c448e..b0b39083aabd 100644 --- a/x-pack/plugins/aiops/public/ui_actions/index.ts +++ b/x-pack/plugins/aiops/public/ui_actions/index.ts @@ -16,7 +16,7 @@ import type { CoreStart } from '@kbn/core/public'; import { createAddChangePointChartAction } from './create_change_point_chart'; import { createOpenChangePointInMlAppAction } from './open_change_point_ml'; import type { AiopsPluginStartDeps } from '../types'; -import { createCategorizeFieldAction } from '../components/log_categorization'; +import { createCategorizeFieldAction } from '../components/log_categorization/categorize_field_actions'; import { createAddPatternAnalysisEmbeddableAction } from './create_pattern_analysis_action'; import { createAddLogRateAnalysisEmbeddableAction } from './create_log_rate_analysis_actions'; diff --git a/x-pack/plugins/aiops/public/ui_actions/open_change_point_ml.tsx b/x-pack/plugins/aiops/public/ui_actions/open_change_point_ml.tsx index 8d2e4f1bbd08..3d52d34a72b8 100644 --- a/x-pack/plugins/aiops/public/ui_actions/open_change_point_ml.tsx +++ b/x-pack/plugins/aiops/public/ui_actions/open_change_point_ml.tsx @@ -10,17 +10,17 @@ import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import { i18n } from '@kbn/i18n'; import type { CoreStart } from '@kbn/core/public'; import type { TimeRange } from '@kbn/es-query'; -import { apiHasParentApi, apiPublishesTimeRange } from '@kbn/presentation-publishing'; import type { ChangePointEmbeddableApi } from '../embeddables/change_point_chart/types'; import type { AiopsPluginStartDeps } from '../types'; import type { ChangePointChartActionContext } from './change_point_action_context'; -import { isChangePointChartEmbeddableContext } from './change_point_action_context'; export const OPEN_CHANGE_POINT_IN_ML_APP_ACTION = 'openChangePointInMlAppAction'; -export const getEmbeddableTimeRange = ( +const getEmbeddableTimeRange = async ( embeddable: ChangePointEmbeddableApi -): TimeRange | undefined => { +): Promise => { + const { apiHasParentApi, apiPublishesTimeRange } = await import('@kbn/presentation-publishing'); + let timeRange = embeddable.timeRange$?.getValue(); if (!timeRange && apiHasParentApi(embeddable) && apiPublishesTimeRange(embeddable.parentApi)) { @@ -45,6 +45,7 @@ export function createOpenChangePointInMlAppAction( defaultMessage: 'Open in AIOps Labs', }), async getHref(context): Promise { + const { isChangePointChartEmbeddableContext } = await import('./change_point_action_context'); if (!isChangePointChartEmbeddableContext(context)) { throw new IncompatibleActionError(); } @@ -57,7 +58,7 @@ export function createOpenChangePointInMlAppAction( page: 'aiops/change_point_detection', pageState: { index: dataViewId.getValue(), - timeRange: getEmbeddableTimeRange(context.embeddable), + timeRange: await getEmbeddableTimeRange(context.embeddable), fieldConfigs: [ { fn: fn.getValue(), @@ -69,6 +70,7 @@ export function createOpenChangePointInMlAppAction( }); }, async execute(context) { + const { isChangePointChartEmbeddableContext } = await import('./change_point_action_context'); if (!isChangePointChartEmbeddableContext(context)) { throw new IncompatibleActionError(); } @@ -78,6 +80,7 @@ export function createOpenChangePointInMlAppAction( } }, async isCompatible(context) { + const { isChangePointChartEmbeddableContext } = await import('./change_point_action_context'); return isChangePointChartEmbeddableContext(context); }, };