-
Notifications
You must be signed in to change notification settings - Fork 8.5k
Remove dashboard performance measurement gap #243097
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
base: main
Are you sure you want to change the base?
Remove dashboard performance measurement gap #243097
Conversation
…mon/kibana into remove-dashboard-measurement-gap
|
@elasticmachine merge upstream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach makes sense to me! I'd like to request @afharo 's sanity check so leaving this as a comment and one minor change request. Nice work!
EDIT: I'll also ask @maryam-saeidi
| if (currentAppId && currentAppId !== app.id) { | ||
| this.appInternalStates.delete(currentAppId); | ||
| } | ||
| window.performance.mark('kbnLoad', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use the const KBN_LOAD_MARKS and LOAD_FIRST_NAV here? Would make it clear that these performance.mark calls are linked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried, but it created a circular dependency since the KBN_LOAD_MARKS lives in the root package. If you want, I could move it to the application package. LMK
|
From my side, these changes LGTM! |
|
@elasticmachine merge upstream |
|
@elasticmachine merge upstream |
|
There are no new commits on the base branch. |
| }) { | ||
| const creationStartTime = performance.now(); | ||
| const creationStartTime = performance.getEntriesByName('dashboard_app_mount', 'mark')[0] | ||
| .startTime; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only works for the first dashboard loaded in a session. What happens if the user loads another dashboard? Then that dashboard load time would be pegged to application mount. Should dashboard_app_mount be cleared after the first load and all subsequent loads use performance.now()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for raising. This mount method is called every time the dashboard app is loaded, but I see it includes the listing page, too, so I'll definitely need to adjust.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in ff7ced4
…mon/kibana into remove-dashboard-measurement-gap
| // @ts-ignore | ||
| expect(getDashboardApiMock.mock.calls[0][0].initialState).toEqual(DEFAULT_DASHBOARD_STATE); | ||
|
|
||
| expect(window.performance.getEntriesByName).toHaveBeenCalledWith( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you move these expects into a new test case. This test case is "should get initialState from saved object" so it does not make sense to assert performance tracking. I would recommend a new test case with the name "should start performance tracking on load" or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about the existing reports initial and subsequent loads? Clearing the mark feels like part of that because it should happen whenever we report a load.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in 74ec9b6
LMK if you want something else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
74ec9b6 made changes in src/platform/plugins/shared/dashboard/public/dashboard_api/performance/query_performance_tracking.test.ts. This comment is about src/platform/plugins/shared/dashboard/public/dashboard_api/load_dashboard_api/load_dashboard_api.test.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad. Okay, check 79aa7c4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still some artifacts from your first changes and the test does not makes sense in the initialState describe block. How about you try the below
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { DEFAULT_DASHBOARD_STATE } from '../default_dashboard_state';
import { loadDashboardApi } from './load_dashboard_api';
jest.mock('../performance/query_performance_tracking', () => {
return {
startQueryPerformanceTracking: jest.fn(),
};
});
import { startQueryPerformanceTracking } from '../performance/query_performance_tracking';
import { DASHBOARD_DURATION_START_MARK } from '../performance/dashboard_duration_start_mark';
jest.mock('@kbn/content-management-content-insights-public', () => {
class ContentInsightsClientMock {
track() {}
}
return {
ContentInsightsClient: ContentInsightsClientMock,
};
});
jest.mock('../../dashboard_client', () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const defaultState = require('../default_dashboard_state');
return {
dashboardClient: {
get: jest.fn().mockResolvedValue({
data: { ...defaultState.DEFAULT_DASHBOARD_STATE },
}),
},
};
});
const lastSavedQuery = { query: 'memory:>220000', language: 'kuery' };
describe('loadDashboardApi', () => {
const getDashboardApiMock = jest.fn();
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('../get_dashboard_api').getDashboardApi = getDashboardApiMock;
getDashboardApiMock.mockReturnValue({
api: {},
cleanUp: jest.fn(),
internalApi: {},
});
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('../../services/dashboard_backup_service').getDashboardBackupService = () => ({
getState: () => ({
query: lastSavedQuery,
}),
});
window.performance.getEntriesByName = jest.fn().mockReturnValue([
{
startTime: 12345,
},
]);
});
afterEach(() => {
jest.resetAllMocks();
});
describe('initialState', () => {
test('should get initialState from saved object', async () => {
await loadDashboardApi({
getCreationOptions: async () => ({
useSessionStorageIntegration: false,
}),
savedObjectId: '12345',
});
expect(getDashboardApiMock).toHaveBeenCalled();
// @ts-ignore
expect(getDashboardApiMock.mock.calls[0][0].initialState).toEqual(DEFAULT_DASHBOARD_STATE);
});
test('should overwrite saved object state with unsaved state', async () => {
await loadDashboardApi({
getCreationOptions: async () => ({
useSessionStorageIntegration: true,
}),
savedObjectId: '12345',
});
expect(getDashboardApiMock).toHaveBeenCalled();
// @ts-ignore
expect(getDashboardApiMock.mock.calls[0][0].initialState).toEqual({
...DEFAULT_DASHBOARD_STATE,
query: lastSavedQuery,
});
});
// dashboard app passes URL state as override state
test('should overwrite saved object state and unsaved state with override state', async () => {
const queryFromUrl = { query: 'memory:>5000', language: 'kuery' };
await loadDashboardApi({
getCreationOptions: async () => ({
useSessionStorageIntegration: true,
getInitialInput: () => ({
query: queryFromUrl,
}),
}),
savedObjectId: '12345',
});
expect(getDashboardApiMock).toHaveBeenCalled();
// @ts-ignore
expect(getDashboardApiMock.mock.calls[0][0].initialState).toEqual({
...DEFAULT_DASHBOARD_STATE,
query: queryFromUrl,
});
});
});
describe('performance monitoring', () => {
test('should start performance tracking on load', async () => {
await loadDashboardApi({
getCreationOptions: async () => ({
useSessionStorageIntegration: false,
}),
savedObjectId: '12345',
});
expect(window.performance.getEntriesByName).toHaveBeenCalledWith(
DASHBOARD_DURATION_START_MARK,
'mark'
);
expect(startQueryPerformanceTracking).toHaveBeenCalledWith(expect.any(Object), {
firstLoad: true,
creationStartTime: 12345,
});
});
});
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
@elasticmachine merge upstream |
…mon/kibana into remove-dashboard-measurement-gap
nreese
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kibana-presentation changes LGTM - thanks for updating the metrics to be more accurate.
code review only
💔 Build Failed
Failed CI StepsTest Failures
Metrics [docs]Module Count
Async chunks
Page load bundle
History
|
|
Thanks for your help, @nreese ! |
Summary
This change corrects the accuracy of two related EBT metrics.
kbnLoad— supposed to end the moment core gives over control to the applicationdashboard_durationsupposed to start the moment the dashboard app is mountedmountmethod, leaving some work unaccounted for (~150ms for a fresh load)Before
notice the gap in the measures
After
perfect stacking