[APM] Reduce initially loaded bundles by 98% (#191658)

## 📓 Summary

Closes #191600 

Moving on invocation site the dynamic import to instantiate the apm
locator and registering the embeddable removed the async import on the
biggest APM js chunks.

In practice, we passed from loading ~900kb of JS to ~10kb, with a total
reduction of about **~16%** of all the code loaded by Kibana for any
page.

There is also deeper code splitting for the APM routes to only load
specific features when required on while using the APM app.

| Before | After |
|--------|--------|
| <img width="622" alt="Screenshot 2024-08-28 at 17 58 59"
src="https://github.com/user-attachments/assets/55198b98-0026-41aa-a7aa-d274b854257f">
| <img width="670" alt="Screenshot 2024-08-28 at 17 59 30"
src="https://github.com/user-attachments/assets/3c9136ea-4437-45e8-b3e6-3fe8555ec7e1">
|

## Mandatory celebration GIF

![](https://media1.tenor.com/m/gRAe8dfvrKMAAAAC/television-tv-shows.gif)

---------

Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Marco Antonio Ghiani 2024-09-03 14:46:19 +02:00 committed by GitHub
parent 5d77095785
commit b7a909f3e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 127 additions and 65 deletions

View file

@ -38,6 +38,7 @@ describe('Dependencies', () => {
describe('top-level dependencies page', () => {
it('has a list of dependencies and you can navigate to the page for one', () => {
cy.visitKibana(`/app/apm/services?${new URLSearchParams(timeRange)}`);
cy.getByTestSubj('superDatePickerstartDatePopoverButton');
cy.contains('nav a', 'Dependencies').click();
// `force: true` because Cypress says the element is 0x0

View file

@ -9,24 +9,48 @@ import { toBooleanRt, toNumberRt } from '@kbn/io-ts-utils';
import { Outlet } from '@kbn/typed-react-router-config';
import * as t from 'io-ts';
import React, { ComponentProps } from 'react';
import { dynamic } from '@kbn/shared-ux-utility';
import { offsetRt } from '../../../../common/comparison_rt';
import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values';
import { environmentRt } from '../../../../common/environment_rt';
import { TraceSearchType } from '../../../../common/trace_explorer';
import { ApmTimeRangeMetadataContextProvider } from '../../../context/time_range_metadata/time_range_metadata_context';
import { ServiceInventory } from '../../app/service_inventory';
import { ServiceMapHome } from '../../app/service_map';
import { TopTracesOverview } from '../../app/top_traces_overview';
import { TraceExplorer } from '../../app/trace_explorer';
import { TraceExplorerAggregatedCriticalPath } from '../../app/trace_explorer/trace_explorer_aggregated_critical_path';
import { TraceExplorerWaterfall } from '../../app/trace_explorer/trace_explorer_waterfall';
import { TraceOverview } from '../../app/trace_overview';
import { TransactionTab } from '../../app/transaction_details/waterfall_with_summary/transaction_tabs';
import { RedirectTo } from '../redirect_to';
import { ServiceGroupTemplate } from '../templates/service_group_template';
import { dependencies } from './dependencies';
import { legacyBackends } from './legacy_backends';
import { storageExplorer } from './storage_explorer';
import { TransactionTab } from '../../app/transaction_details/waterfall_with_summary/transaction_tabs';
const ServiceGroupTemplate = dynamic(() =>
import('../templates/service_group_template').then((mod) => ({
default: mod.ServiceGroupTemplate,
}))
);
const ServiceInventory = dynamic(() =>
import('../../app/service_inventory').then((mod) => ({ default: mod.ServiceInventory }))
);
const ServiceMapHome = dynamic(() =>
import('../../app/service_map').then((mod) => ({ default: mod.ServiceMapHome }))
);
const TopTracesOverview = dynamic(() =>
import('../../app/top_traces_overview').then((mod) => ({ default: mod.TopTracesOverview }))
);
const TraceExplorer = dynamic(() =>
import('../../app/trace_explorer').then((mod) => ({ default: mod.TraceExplorer }))
);
const TraceExplorerAggregatedCriticalPath = dynamic(() =>
import('../../app/trace_explorer/trace_explorer_aggregated_critical_path').then((mod) => ({
default: mod.TraceExplorerAggregatedCriticalPath,
}))
);
const TraceExplorerWaterfall = dynamic(() =>
import('../../app/trace_explorer/trace_explorer_waterfall').then((mod) => ({
default: mod.TraceExplorerWaterfall,
}))
);
const TraceOverview = dynamic(() =>
import('../../app/trace_overview').then((mod) => ({ default: mod.TraceOverview }))
);
function serviceGroupPage<TPath extends string>({
path,

View file

@ -9,7 +9,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import * as t from 'io-ts';
import { EuiLink } from '@elastic/eui';
import { StorageExplorer } from '../../app/storage_explorer';
import { dynamic } from '@kbn/shared-ux-utility';
import { ApmMainTemplate } from '../templates/apm_main_template';
import { Breadcrumb } from '../../app/breadcrumb';
import {
@ -18,6 +18,10 @@ import {
} from '../../../../common/storage_explorer_types';
import { getStorageExplorerFeedbackHref } from '../../app/storage_explorer/get_storage_explorer_links';
const StorageExplorer = dynamic(() =>
import('../../app/storage_explorer').then((mod) => ({ default: mod.StorageExplorer }))
);
export const storageExplorer = {
'/storage-explorer': {
element: (

View file

@ -7,9 +7,13 @@
import * as t from 'io-ts';
import React from 'react';
import { Onboarding } from '../../app/onboarding';
import { dynamic } from '@kbn/shared-ux-utility';
import { INSTRUCTION_VARIANT } from '../../app/onboarding/instruction_variants';
const Onboarding = dynamic(() =>
import('../../app/onboarding').then((mod) => ({ default: mod.Onboarding }))
);
export const onboarding = {
'/onboarding': {
element: <Onboarding />,

View file

@ -12,6 +12,7 @@ import * as t from 'io-ts';
import qs from 'query-string';
import React from 'react';
import { Redirect } from 'react-router-dom';
import { dynamic } from '@kbn/shared-ux-utility';
import { offsetRt } from '../../../../common/comparison_rt';
import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values';
import { environmentRt } from '../../../../common/environment_rt';
@ -21,25 +22,47 @@ import {
} from '../../../../common/latency_aggregation_types';
import { ApmTimeRangeMetadataContextProvider } from '../../../context/time_range_metadata/time_range_metadata_context';
import { useApmParams } from '../../../hooks/use_apm_params';
import { AlertsOverview, ALERT_STATUS_ALL } from '../../app/alerts_overview';
import { ErrorGroupDetails } from '../../app/error_group_details';
import { ErrorGroupOverview } from '../../app/error_group_overview';
import { InfraOverview } from '../../app/infra_overview';
import { ALERT_STATUS_ALL, AlertsOverview } from '../../app/alerts_overview';
import { InfraTab } from '../../app/infra_overview/infra_tabs/use_tabs';
import { Metrics } from '../../app/metrics';
import { MetricsDetails } from '../../app/metrics_details';
import { ServiceDependencies } from '../../app/service_dependencies';
import { ServiceLogs } from '../../app/service_logs';
import { ServiceMapServiceDetail } from '../../app/service_map';
import { ServiceOverview } from '../../app/service_overview';
import { TransactionDetails } from '../../app/transaction_details';
import { TransactionOverview } from '../../app/transaction_overview';
import { ApmServiceTemplate } from '../templates/apm_service_template';
import { ApmServiceWrapper } from './apm_service_wrapper';
import { RedirectToDefaultServiceRouteView } from './redirect_to_default_service_route_view';
import { ProfilingOverview } from '../../app/profiling_overview';
import { SearchBar } from '../../shared/search_bar/search_bar';
import { ServiceDependencies } from '../../app/service_dependencies';
import { ServiceDashboards } from '../../app/service_dashboards';
import { ErrorGroupDetails } from '../../app/error_group_details';
const ErrorGroupOverview = dynamic(() =>
import('../../app/error_group_overview').then((mod) => ({ default: mod.ErrorGroupOverview }))
);
const InfraOverview = dynamic(() =>
import('../../app/infra_overview').then((mod) => ({ default: mod.InfraOverview }))
);
const Metrics = dynamic(() =>
import('../../app/metrics').then((mod) => ({ default: mod.Metrics }))
);
const MetricsDetails = dynamic(() =>
import('../../app/metrics_details').then((mod) => ({ default: mod.MetricsDetails }))
);
const ServiceLogs = dynamic(() =>
import('../../app/service_logs').then((mod) => ({ default: mod.ServiceLogs }))
);
const ServiceMapServiceDetail = dynamic(() =>
import('../../app/service_map').then((mod) => ({ default: mod.ServiceMapServiceDetail }))
);
const ServiceOverview = dynamic(() =>
import('../../app/service_overview').then((mod) => ({ default: mod.ServiceOverview }))
);
const TransactionDetails = dynamic(() =>
import('../../app/transaction_details').then((mod) => ({ default: mod.TransactionDetails }))
);
const TransactionOverview = dynamic(() =>
import('../../app/transaction_overview').then((mod) => ({ default: mod.TransactionOverview }))
);
const ProfilingOverview = dynamic(() =>
import('../../app/profiling_overview').then((mod) => ({ default: mod.ProfilingOverview }))
);
function page({
title,

View file

@ -13,9 +13,7 @@ import type { EmbeddableApmAlertingVizProps } from '../types';
import type { EmbeddableDeps } from '../../types';
import { ApmEmbeddableContext } from '../../embeddable_context';
import { APMAlertingFailedTransactionsChart } from './chart';
export const APM_ALERTING_FAILED_TRANSACTIONS_CHART_EMBEDDABLE =
'APM_ALERTING_FAILED_TRANSACTIONS_CHART_EMBEDDABLE';
import { APM_ALERTING_FAILED_TRANSACTIONS_CHART_EMBEDDABLE } from '../constants';
export const getApmAlertingFailedTransactionsChartEmbeddableFactory = (deps: EmbeddableDeps) => {
const factory: ReactEmbeddableFactory<

View file

@ -13,8 +13,7 @@ import type { EmbeddableApmAlertingLatencyVizProps } from '../types';
import type { EmbeddableDeps } from '../../types';
import { ApmEmbeddableContext } from '../../embeddable_context';
import { APMAlertingLatencyChart } from './chart';
export const APM_ALERTING_LATENCY_CHART_EMBEDDABLE = 'APM_ALERTING_LATENCY_CHART_EMBEDDABLE';
import { APM_ALERTING_LATENCY_CHART_EMBEDDABLE } from '../constants';
export const getApmAlertingLatencyChartEmbeddableFactory = (deps: EmbeddableDeps) => {
const factory: ReactEmbeddableFactory<

View file

@ -13,8 +13,7 @@ import type { EmbeddableApmAlertingVizProps } from '../types';
import type { EmbeddableDeps } from '../../types';
import { ApmEmbeddableContext } from '../../embeddable_context';
import { APMAlertingThroughputChart } from './chart';
export const APM_ALERTING_THROUGHPUT_CHART_EMBEDDABLE = 'APM_ALERTING_THROUGHPUT_CHART_EMBEDDABLE';
import { APM_ALERTING_THROUGHPUT_CHART_EMBEDDABLE } from '../constants';
export const getApmAlertingThroughputChartEmbeddableFactory = (deps: EmbeddableDeps) => {
const factory: ReactEmbeddableFactory<

View file

@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export const APM_ALERTING_FAILED_TRANSACTIONS_CHART_EMBEDDABLE =
'APM_ALERTING_FAILED_TRANSACTIONS_CHART_EMBEDDABLE';
export const APM_ALERTING_LATENCY_CHART_EMBEDDABLE = 'APM_ALERTING_LATENCY_CHART_EMBEDDABLE';
export const APM_ALERTING_THROUGHPUT_CHART_EMBEDDABLE = 'APM_ALERTING_THROUGHPUT_CHART_EMBEDDABLE';

View file

@ -8,6 +8,11 @@ import { CoreSetup } from '@kbn/core/public';
import { ApmPluginStartDeps, ApmPluginStart } from '../plugin';
import { EmbeddableDeps } from './types';
import {
APM_ALERTING_FAILED_TRANSACTIONS_CHART_EMBEDDABLE,
APM_ALERTING_LATENCY_CHART_EMBEDDABLE,
APM_ALERTING_THROUGHPUT_CHART_EMBEDDABLE,
} from './alerting/constants';
export async function registerEmbeddables(
deps: Omit<EmbeddableDeps, 'coreStart' | 'pluginsStart'>
@ -16,36 +21,32 @@ export async function registerEmbeddables(
const pluginsSetup = deps.pluginsSetup;
const [coreStart, pluginsStart] = await coreSetup.getStartServices();
const registerReactEmbeddableFactory = pluginsSetup.embeddable.registerReactEmbeddableFactory;
const registerApmAlertingLatencyChartEmbeddable = async () => {
const { getApmAlertingLatencyChartEmbeddableFactory, APM_ALERTING_LATENCY_CHART_EMBEDDABLE } =
await import('./alerting/alerting_latency_chart/react_embeddable_factory');
registerReactEmbeddableFactory(APM_ALERTING_LATENCY_CHART_EMBEDDABLE, async () => {
return getApmAlertingLatencyChartEmbeddableFactory({ ...deps, coreStart, pluginsStart });
registerReactEmbeddableFactory(APM_ALERTING_LATENCY_CHART_EMBEDDABLE, async () => {
const { getApmAlertingLatencyChartEmbeddableFactory } = await import(
'./alerting/alerting_latency_chart/react_embeddable_factory'
);
return getApmAlertingLatencyChartEmbeddableFactory({ ...deps, coreStart, pluginsStart });
});
registerReactEmbeddableFactory(APM_ALERTING_THROUGHPUT_CHART_EMBEDDABLE, async () => {
const { getApmAlertingThroughputChartEmbeddableFactory } = await import(
'./alerting/alerting_throughput_chart/react_embeddable_factory'
);
return getApmAlertingThroughputChartEmbeddableFactory({ ...deps, coreStart, pluginsStart });
});
registerReactEmbeddableFactory(APM_ALERTING_FAILED_TRANSACTIONS_CHART_EMBEDDABLE, async () => {
const { getApmAlertingFailedTransactionsChartEmbeddableFactory } = await import(
'./alerting/alerting_failed_transactions_chart/react_embeddable_factory'
);
return getApmAlertingFailedTransactionsChartEmbeddableFactory({
...deps,
coreStart,
pluginsStart,
});
};
const registerApmAlertingThroughputChartEmbeddable = async () => {
const {
getApmAlertingThroughputChartEmbeddableFactory,
APM_ALERTING_THROUGHPUT_CHART_EMBEDDABLE,
} = await import('./alerting/alerting_throughput_chart/react_embeddable_factory');
registerReactEmbeddableFactory(APM_ALERTING_THROUGHPUT_CHART_EMBEDDABLE, async () => {
return getApmAlertingThroughputChartEmbeddableFactory({ ...deps, coreStart, pluginsStart });
});
};
const registerApmAlertingFailedTransactionsChartEmbeddable = async () => {
const {
getApmAlertingFailedTransactionsChartEmbeddableFactory,
APM_ALERTING_FAILED_TRANSACTIONS_CHART_EMBEDDABLE,
} = await import('./alerting/alerting_failed_transactions_chart/react_embeddable_factory');
registerReactEmbeddableFactory(APM_ALERTING_FAILED_TRANSACTIONS_CHART_EMBEDDABLE, async () => {
return getApmAlertingFailedTransactionsChartEmbeddableFactory({
...deps,
coreStart,
pluginsStart,
});
});
};
registerApmAlertingLatencyChartEmbeddable();
registerApmAlertingThroughputChartEmbeddable();
registerApmAlertingFailedTransactionsChartEmbeddable();
});
}

View file

@ -15,8 +15,6 @@ import { ENVIRONMENT_ALL } from '../../common/environment_filter_values';
import type { TimePickerTimeDefaults } from '../components/shared/date_picker/typings';
import type { APMLocatorPayload } from './helpers';
const helpersModule = import('./helpers');
export const APM_APP_LOCATOR_ID = 'APM_LOCATOR';
export class APMServiceDetailLocator implements LocatorDefinition<APMLocatorPayload> {
@ -28,7 +26,7 @@ export class APMServiceDetailLocator implements LocatorDefinition<APMLocatorPayl
}
async getLocation(payload: APMLocatorPayload) {
const { getPathForServiceDetail } = await helpersModule;
const { getPathForServiceDetail } = await import('./helpers');
const defaultTimeRange = this.uiSettings.get<TimePickerTimeDefaults>(
UI_SETTINGS.TIMEPICKER_TIME_DEFAULTS