diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index e36abd8d023e4..c83af688ed47f 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -400,6 +400,7 @@ enabled: - x-pack/performance/journeys/flight_dashboard.ts - x-pack/performance/journeys/login.ts - x-pack/performance/journeys/many_fields_discover.ts + - x-pack/performance/journeys/many_fields_transform.ts - x-pack/performance/journeys/promotion_tracking_dashboard.ts - x-pack/performance/journeys/web_logs_dashboard.ts - x-pack/performance/journeys/data_stress_test_lens.ts diff --git a/dev_docs/tutorials/performance/adding_performance_journey.mdx b/dev_docs/tutorials/performance/adding_performance_journey.mdx index 423619defd063..77bb7d6f39e53 100644 --- a/dev_docs/tutorials/performance/adding_performance_journey.mdx +++ b/dev_docs/tutorials/performance/adding_performance_journey.mdx @@ -12,7 +12,7 @@ In order to achieve our goal of creating best user experience in Kibana, it is i To make things easier, we introduced performance journeys, that mimics end-user experience with Kibana. Journey runs a flow of user interactions with Kibana in a browser and collects APM metrics for both server and client-side. -It is possible to instrument Kibana with [custom performance metrics](https://docs.elastic.dev/kibana-dev-docs/tutorials/performance/adding_custom_performance_metrics), +It is possible to instrument Kibana with [custom performance metrics](https://docs.elastic.dev/kibana-dev-docs/tutorial/performance/adding_custom_performance_metrics), that will provide more detailed information about feature performance. Journeys core is [kbn-journeys](packages/kbn-journeys/README.mdx) package. It is a function test by design and is powered @@ -61,7 +61,7 @@ Scripts steps include: You can skip warmup phase for debug purpose by using `--skip-warmup` flag -Since the tests are run on a local machine, there is also realistic throttling applied to the network to +Since the tests are run on a local machine, there is also realistic throttling applied to the network to simulate real life internet connection. This means that all requests have a fixed latency and limited bandwidth. ### Benchmarking performance on CI diff --git a/packages/kbn-journeys/journey/journey_ftr_harness.ts b/packages/kbn-journeys/journey/journey_ftr_harness.ts index bf5af70f3d698..1a227db49f11e 100644 --- a/packages/kbn-journeys/journey/journey_ftr_harness.ts +++ b/packages/kbn-journeys/journey/journey_ftr_harness.ts @@ -240,9 +240,8 @@ export class JourneyFtrHarness { return await block(); } - const span = this.apm?.startSpan(name, type ?? null, { - childOf: this.currentTransaction, - }); + const span = this.currentTransaction.startSpan(name, type ?? null); + if (!span) { return await block(); } diff --git a/x-pack/performance/journeys/many_fields_transform.ts b/x-pack/performance/journeys/many_fields_transform.ts new file mode 100644 index 0000000000000..24659d3cbfca8 --- /dev/null +++ b/x-pack/performance/journeys/many_fields_transform.ts @@ -0,0 +1,32 @@ +/* + * 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 { Journey } from '@kbn/journeys'; +import { subj } from '@kbn/test-subj-selector'; +import { waitForChrome } from '../utils'; + +export const journey = new Journey({ + kbnArchives: ['test/functional/fixtures/kbn_archiver/many_fields_data_view'], + esArchives: ['test/functional/fixtures/es_archiver/many_fields'], +}) + .step('Go to Transforms', async ({ page, kbnUrl }) => { + await page.goto(kbnUrl.get(`app/management/data/transform`)); + await waitForChrome(page); + await page.waitForSelector(subj('transformCreateFirstButton')); + await page.waitForSelector(subj('globalLoadingIndicator-hidden')); + }) + .step('Go to data view selection', async ({ page }) => { + const createButtons = page.locator(subj('transformCreateFirstButton')); + await createButtons.first().click(); + await page.waitForSelector(subj('savedObjectsFinderTable')); + }) + .step('Go to Transform Wizard', async ({ page }) => { + await page.click(subj('savedObjectTitleindices-stats*')); + // Extended the timeout, this one tracks a known issue with slow data grid performance with many fields + await page.waitForSelector(subj('transformIndexPreview loaded'), { timeout: 120000 }); + await page.waitForSelector(subj('globalLoadingIndicator-hidden'), { timeout: 120000 }); + }); diff --git a/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx b/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx index c779fe577310e..da8fc9ede7344 100644 --- a/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx +++ b/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx @@ -71,6 +71,7 @@ dataStart.search.search = jest.fn(({ params }: IKibanaSearchRequest) => { }) as ISearchGeneric; const appDependencies: AppDependencies = { + analytics: coreStart.analytics, application: coreStart.application, charts: chartPluginMock.createStartContract(), chrome: coreStart.chrome, diff --git a/x-pack/plugins/transform/public/app/app_dependencies.tsx b/x-pack/plugins/transform/public/app/app_dependencies.tsx index 0830a741f45c6..d637fc706eb44 100644 --- a/x-pack/plugins/transform/public/app/app_dependencies.tsx +++ b/x-pack/plugins/transform/public/app/app_dependencies.tsx @@ -6,6 +6,7 @@ */ import type { + AnalyticsServiceStart, ApplicationStart, ChromeStart, DocLinksStart, @@ -15,12 +16,12 @@ import type { NotificationsStart, OverlayStart, SavedObjectsStart, + ScopedHistory, ThemeServiceStart, } from '@kbn/core/public'; import type { SavedObjectsStart as SavedObjectsPluginStart } from '@kbn/saved-objects-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import type { ScopedHistory } from '@kbn/core/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; @@ -38,6 +39,7 @@ import type { SettingsStart } from '@kbn/core-ui-settings-browser'; import type { GetMlSharedImportsReturnType } from '../shared_imports'; export interface AppDependencies { + analytics: AnalyticsServiceStart; application: ApplicationStart; charts: ChartsPluginStart; chrome: ChromeStart; diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts index ef5c14265bd49..e3de879dfc40e 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { EuiDataGridColumn } from '@elastic/eui'; +import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { isRuntimeMappings } from '@kbn/ml-runtime-field-utils'; import { buildBaseFilterCriteria } from '@kbn/ml-query-utils'; import { @@ -39,7 +40,7 @@ import { import { getErrorMessage } from '../../../common/utils/errors'; import { isDefaultQuery, matchAllQuery, TransformConfigQuery } from '../common'; -import { useToastNotifications } from '../app_dependencies'; +import { useToastNotifications, useAppDependencies } from '../app_dependencies'; import type { StepDefineExposedState } from '../sections/create_transform/components/step_define/common'; import { SearchItems } from './use_search_items'; @@ -52,6 +53,12 @@ export const useIndexData = ( combinedRuntimeMappings?: StepDefineExposedState['runtimeMappings'], timeRangeMs?: TimeRangeMs ): UseIndexDataReturnType => { + const { analytics } = useAppDependencies(); + + // Store the performance metric's start time using a ref + // to be able to track it across rerenders. + const loadIndexDataStartTime = useRef(window.performance.now()); + const indexPattern = useMemo(() => dataView.getIndexPattern(), [dataView]); const api = useApi(); @@ -315,6 +322,22 @@ export const useIndexData = ( const renderCellValue = useRenderCellValue(dataView, pagination, tableItems); + if ( + dataGrid.status === INDEX_STATUS.LOADED && + dataViewFields !== undefined && + loadIndexDataStartTime.current !== undefined + ) { + const loadIndexDataDuration = window.performance.now() - loadIndexDataStartTime.current; + + // Set this to undefined so reporting the metric gets triggered only once. + loadIndexDataStartTime.current = undefined; + + reportPerformanceMetricEvent(analytics, { + eventName: 'transformLoadIndexPreview', + duration: loadIndexDataDuration, + }); + } + return { ...dataGrid, renderCellValue, diff --git a/x-pack/plugins/transform/public/app/mount_management_section.ts b/x-pack/plugins/transform/public/app/mount_management_section.ts index 9568be198253e..f2b5663154e79 100644 --- a/x-pack/plugins/transform/public/app/mount_management_section.ts +++ b/x-pack/plugins/transform/public/app/mount_management_section.ts @@ -29,6 +29,7 @@ export async function mountManagementSection( const startServices = await getStartServices(); const [core, plugins] = startServices; const { + analytics, application, chrome, docLinks, @@ -61,6 +62,7 @@ export async function mountManagementSection( // AppCore/AppPlugins to be passed on as React context const appDependencies: AppDependencies = { + analytics, application, chrome, data, diff --git a/x-pack/plugins/transform/tsconfig.json b/x-pack/plugins/transform/tsconfig.json index 0103e7e4528c1..2be11c9b9d48f 100644 --- a/x-pack/plugins/transform/tsconfig.json +++ b/x-pack/plugins/transform/tsconfig.json @@ -65,7 +65,8 @@ "@kbn/ml-runtime-field-utils", "@kbn/ml-date-utils", "@kbn/saved-search-plugin", - "@kbn/unified-field-list" + "@kbn/unified-field-list", + "@kbn/ebt-tools" ], "exclude": [ "target/**/*",