Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Dataset Quality] Optimize bundle size and chunks fragmentation #194661

Merged
merged 3 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useMemo } from 'react';
import { CoreStart } from '@kbn/core/public';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { dynamic } from '@kbn/shared-ux-utility';
import { CoreStart } from '@kbn/core/public';
import { PerformanceContextProvider } from '@kbn/ebt-tools';
import { DatasetQualityContext, DatasetQualityContextValue } from './context';
import { useKibanaContextForPluginProvider } from '../../utils';
import { DatasetQualityStartDeps } from '../../types';
import React, { useMemo } from 'react';
import { DatasetQualityController } from '../../controller/dataset_quality';
import SummaryPanelProvider from '../../hooks/use_summary_panel';
import { ITelemetryClient } from '../../services/telemetry';
import { DatasetQualityStartDeps } from '../../types';
import { useKibanaContextForPluginProvider } from '../../utils';
import { DatasetQualityContext, DatasetQualityContextValue } from './context';
import EmptyStateWrapper from './empty_state/empty_state';
import Filters from './filters/filters';
import Header from './header';
import SummaryPanel from './summary_panel/summary_panel';
import Table from './table/table';
import Warnings from './warnings/warnings';

export interface DatasetQualityProps {
controller: DatasetQualityController;
Expand All @@ -25,45 +31,36 @@ export interface CreateDatasetQualityArgs {
telemetryClient: ITelemetryClient;
}

export const createDatasetQuality = ({
export const DatasetQuality = ({
controller,
core,
plugins,
telemetryClient,
}: CreateDatasetQualityArgs) => {
return ({ controller }: DatasetQualityProps) => {
const SummaryPanelProvider = dynamic(() => import('../../hooks/use_summary_panel'));
const KibanaContextProviderForPlugin = useKibanaContextForPluginProvider(core, plugins);
}: DatasetQualityProps & CreateDatasetQualityArgs) => {
const KibanaContextProviderForPlugin = useKibanaContextForPluginProvider(core, plugins);

const datasetQualityProviderValue: DatasetQualityContextValue = useMemo(
() => ({
service: controller.service,
telemetryClient,
}),
[controller.service]
);
const datasetQualityProviderValue: DatasetQualityContextValue = useMemo(
() => ({
service: controller.service,
telemetryClient,
}),
[controller.service, telemetryClient]
);

return (
<PerformanceContextProvider>
<DatasetQualityContext.Provider value={datasetQualityProviderValue}>
<SummaryPanelProvider>
<KibanaContextProviderForPlugin>
<DatasetQuality />
</KibanaContextProviderForPlugin>
</SummaryPanelProvider>
</DatasetQualityContext.Provider>
</PerformanceContextProvider>
);
};
return (
<PerformanceContextProvider>
<DatasetQualityContext.Provider value={datasetQualityProviderValue}>
<SummaryPanelProvider>
<KibanaContextProviderForPlugin>
<DatasetQualityContent />
</KibanaContextProviderForPlugin>
</SummaryPanelProvider>
</DatasetQualityContext.Provider>
</PerformanceContextProvider>
);
};

const Header = dynamic(() => import('./header'));
const Warnings = dynamic(() => import('./warnings/warnings'));
const EmptyStateWrapper = dynamic(() => import('./empty_state/empty_state'));
const Table = dynamic(() => import('./table/table'));
const Filters = dynamic(() => import('./filters/filters'));
const SummaryPanel = dynamic(() => import('./summary_panel/summary_panel'));
Comment on lines -59 to -64
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

All of these chunks were loaded in parallel when loading the page, causing an excessive fragmentation for a UI that can loaded at once.

The lazy loading now happens for the main chunk on the index file, where the getter dynamically import the whole UI island.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to better understand what happened here:
Should we use this approach then only for elements in the UI that are showed after some interaction? for example a flyout?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort of, the best way to think about this is to split only when we might not need something.
For instance, in the above case we were splitting into 6 chunks sections of the same page that are always immediately loaded when the page is visited. In this case we could consider it as a single "UI island" that can be dynamically loaded at once, reducing the back and forth with the server.

The flyout example you are doing is a good one, as the user might never open the flyout during a session.


function DatasetQuality() {
function DatasetQualityContent() {
return (
<EuiFlexGroup direction="column" gutterSize="l">
<EuiFlexItem grow={false}>
Expand All @@ -72,7 +69,6 @@ function DatasetQuality() {
<EuiFlexItem grow={false}>
<Warnings />
</EuiFlexItem>

<EmptyStateWrapper>
<EuiFlexItem grow={false}>
<Filters />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.
*/

import React from 'react';
import { dynamic } from '@kbn/shared-ux-utility';
import type { CreateDatasetQualityArgs, DatasetQualityProps } from './dataset_quality';

export type { CreateDatasetQualityArgs, DatasetQualityProps };

const DatasetQuality = dynamic(() =>
import('./dataset_quality').then((mod) => ({ default: mod.DatasetQuality }))
);

export const createDatasetQuality = ({
core,
plugins,
telemetryClient,
}: CreateDatasetQualityArgs) => {
return ({ controller }: DatasetQualityProps) => {
return (
<DatasetQuality
controller={controller}
core={core}
plugins={plugins}
telemetryClient={telemetryClient}
/>
);
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { getDevToolsOptions } from '@kbn/xstate-utils';
import equal from 'fast-deep-equal';
import { distinctUntilChanged, from, map } from 'rxjs';
import { interpret } from 'xstate';
import { IDataStreamsStatsClient } from '../../services/data_streams_stats';
import { DataStreamsStatsServiceStart } from '../../services/data_streams_stats';
import {
createDatasetQualityControllerStateMachine,
DEFAULT_CONTEXT,
Expand All @@ -22,18 +22,20 @@ type InitialState = DatasetQualityPublicStateUpdate;

interface Dependencies {
core: CoreStart;
dataStreamStatsClient: IDataStreamsStatsClient;
dataStreamStatsService: DataStreamsStatsServiceStart;
}

export const createDatasetQualityControllerFactory =
({ core, dataStreamStatsClient }: Dependencies) =>
({ core, dataStreamStatsService }: Dependencies) =>
async ({
initialState = DEFAULT_CONTEXT,
}: {
initialState?: InitialState;
}): Promise<DatasetQualityController> => {
const initialContext = getContextFromPublicState(initialState ?? {});

const dataStreamStatsClient = await dataStreamStatsService.getClient();

const machine = createDatasetQualityControllerStateMachine({
initialContext,
toasts: core.notifications.toasts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,33 @@ import equal from 'fast-deep-equal';
import { distinctUntilChanged, from, map } from 'rxjs';
import { interpret } from 'xstate';
import { createDatasetQualityDetailsControllerStateMachine } from '../../state_machines/dataset_quality_details_controller/state_machine';
import { IDataStreamsStatsClient } from '../../services/data_streams_stats';
import { IDataStreamDetailsClient } from '../../services/data_stream_details';
import { DataStreamsStatsServiceStart } from '../../services/data_streams_stats';
import { DataStreamDetailsServiceStart } from '../../services/data_stream_details';
import { DatasetQualityStartDeps } from '../../types';
import { getContextFromPublicState, getPublicStateFromContext } from './public_state';
import { DatasetQualityDetailsController, DatasetQualityDetailsPublicStateUpdate } from './types';

interface Dependencies {
core: CoreStart;
plugins: DatasetQualityStartDeps;
dataStreamStatsClient: IDataStreamsStatsClient;
dataStreamDetailsClient: IDataStreamDetailsClient;
dataStreamStatsService: DataStreamsStatsServiceStart;
dataStreamDetailsService: DataStreamDetailsServiceStart;
}

export const createDatasetQualityDetailsControllerFactory =
({ core, plugins, dataStreamStatsClient, dataStreamDetailsClient }: Dependencies) =>
({ core, plugins, dataStreamStatsService, dataStreamDetailsService }: Dependencies) =>
async ({
initialState,
}: {
initialState: DatasetQualityDetailsPublicStateUpdate;
}): Promise<DatasetQualityDetailsController> => {
const initialContext = getContextFromPublicState(initialState);

const [dataStreamStatsClient, dataStreamDetailsClient] = await Promise.all([
dataStreamStatsService.getClient(),
dataStreamDetailsService.getClient(),
]);

const machine = createDatasetQualityDetailsControllerStateMachine({
initialContext,
plugins,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,3 @@ export type { DatasetQualityPluginSetup, DatasetQualityPluginStart } from './typ
export function plugin(context: PluginInitializerContext<DatasetQualityConfig>) {
return new DatasetQualityPlugin(context);
}

export { datasetQualityAppTitle } from '../common/translations';
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ export class DatasetQualityPlugin
public start(core: CoreStart, plugins: DatasetQualityStartDeps): DatasetQualityPluginStart {
const telemetryClient = this.telemetry.start();

const dataStreamStatsClient = new DataStreamsStatsService().start({
const dataStreamStatsService = new DataStreamsStatsService().start({
http: core.http,
}).client;
});

const dataStreamDetailsClient = new DataStreamDetailsService().start({
const dataStreamDetailsService = new DataStreamDetailsService().start({
http: core.http,
}).client;
});

const DatasetQuality = createDatasetQuality({
core,
Expand All @@ -52,7 +52,7 @@ export class DatasetQualityPlugin

const createDatasetQualityController = createDatasetQualityControllerLazyFactory({
core,
dataStreamStatsClient,
dataStreamStatsService,
});

const DatasetQualityDetails = createDatasetQualityDetails({
Expand All @@ -64,8 +64,8 @@ export class DatasetQualityPlugin
const createDatasetQualityDetailsController = createDatasetQualityDetailsControllerLazyFactory({
core,
plugins,
dataStreamStatsClient,
dataStreamDetailsClient,
dataStreamStatsService,
dataStreamDetailsService,
});

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,31 @@
* 2.0.
*/

import { DataStreamDetailsClient } from './data_stream_details_client';
import {
DataStreamDetailsServiceSetup,
DataStreamDetailsServiceStartDeps,
DataStreamDetailsServiceStart,
IDataStreamDetailsClient,
} from './types';

export class DataStreamDetailsService {
constructor() {}
private client?: IDataStreamDetailsClient;

public setup(): DataStreamDetailsServiceSetup {}

public start({ http }: DataStreamDetailsServiceStartDeps): DataStreamDetailsServiceStart {
const client = new DataStreamDetailsClient(http);

return {
client,
getClient: () => this.getClient({ http }),
};
}

private async getClient({ http }: DataStreamDetailsServiceStartDeps) {
if (!this.client) {
const { DataStreamDetailsClient } = await import('./data_stream_details_client');
const client = new DataStreamDetailsClient(http);
this.client = client;
}

return this.client;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
* 2.0.
*/

export * from './data_stream_details_client';
export * from './data_stream_details_service';
export * from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { Dashboard, DegradedFieldValues } from '../../../common/api_types';
export type DataStreamDetailsServiceSetup = void;

export interface DataStreamDetailsServiceStart {
client: IDataStreamDetailsClient;
getClient: () => Promise<IDataStreamDetailsClient>;
}

export interface DataStreamDetailsServiceStartDeps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,31 @@
* 2.0.
*/

import { DataStreamsStatsClient } from './data_streams_stats_client';
import {
DataStreamsStatsServiceSetup,
DataStreamsStatsServiceStartDeps,
DataStreamsStatsServiceStart,
IDataStreamsStatsClient,
} from './types';

export class DataStreamsStatsService {
constructor() {}
private client?: IDataStreamsStatsClient;

public setup(): DataStreamsStatsServiceSetup {}

public start({ http }: DataStreamsStatsServiceStartDeps): DataStreamsStatsServiceStart {
const client = new DataStreamsStatsClient(http);

return {
client,
getClient: () => this.getClient({ http }),
};
}

private async getClient({ http }: DataStreamsStatsServiceStartDeps) {
if (!this.client) {
const { DataStreamsStatsClient } = await import('./data_streams_stats_client');
const client = new DataStreamsStatsClient(http);
this.client = client;
}

return this.client;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
* 2.0.
*/

export * from './data_streams_stats_client';
export * from './data_streams_stats_service';
export * from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { NonAggregatableDatasets } from '../../../common/api_types';
export type DataStreamsStatsServiceSetup = void;

export interface DataStreamsStatsServiceStart {
client: IDataStreamsStatsClient;
getClient: () => Promise<IDataStreamsStatsClient>;
}

export interface DataStreamsStatsServiceStartDeps {
Expand Down
Loading