Skip to content

Commit

Permalink
Report page load asset size (#66224)
Browse files Browse the repository at this point in the history
* performance_tests: draft version

* update cli runner and script

* ingest metrics

* save asset type for plugins

* Update src/dev/performance/ingest_metrics.ts

Co-authored-by: Spencer <[email protected]>

* follow review comments

* fix size calc, add FTR config, move src to kbn/test

* fix import, remove unused interface

* Update packages/kbn-test/src/page_load_metrics/capture_page_load_metrics.ts

Co-authored-by: Spencer <[email protected]>

* start chromium with no-sandbox

* add logging

* check page contains expected element, cut apps to 5

* fix locator & typo

* Update packages/kbn-test/src/page_load_metrics/navigation.ts

Co-authored-by: Spencer <[email protected]>

* Update navigation.ts

* Update navigation.ts

* bump puppeteer version

* fix typo

* update navigation script

* update config file

* update yarn.lock

* fix

* take screenshot on failure

* update screenshot title

* fix screenshot saving and error

* invalid locator

* Revert "invalid locator"

This reverts commit 3007539.

* run script in a loop 10 times

* Revert "run script in a loop 10 times"

This reverts commit 6cfa219.

* path config value directly

* fix screenshots directory setup

* update maps locator, common for landing and new map

Co-authored-by: Spencer <[email protected]>
Co-authored-by: Elastic Machine <[email protected]>
Co-authored-by: Mikhail Shustov <[email protected]>
  • Loading branch information
4 people authored Jun 5, 2020
1 parent ade2f03 commit 6f57fa0
Show file tree
Hide file tree
Showing 16 changed files with 2,992 additions and 2 deletions.
1 change: 1 addition & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ kibanaPipeline(timeoutMinutes: 155, checkPrChanges: true) {
'xpack-ciGroup9': kibanaPipeline.xpackCiGroupProcess(9),
'xpack-ciGroup10': kibanaPipeline.xpackCiGroupProcess(10),
'xpack-accessibility': kibanaPipeline.functionalTestProcess('xpack-accessibility', './test/scripts/jenkins_xpack_accessibility.sh'),
'xpack-pageLoadMetrics': kibanaPipeline.functionalTestProcess('xpack-pageLoadMetrics', './test/scripts/jenkins_xpack_page_load_metrics.sh'),
'xpack-securitySolutionCypress': { processNumber ->
whenChanged(['x-pack/plugins/security_solution/', 'x-pack/test/security_solution_cypress/']) {
kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypress', './test/scripts/jenkins_security_solution_cypress.sh')(processNumber)
Expand Down
2 changes: 2 additions & 0 deletions packages/kbn-test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@kbn/babel-preset": "1.0.0",
"@kbn/dev-utils": "1.0.0",
"@types/parse-link-header": "^1.0.0",
"@types/puppeteer": "^3.0.0",
"@types/strip-ansi": "^5.2.1",
"@types/xml2js": "^0.4.5",
"diff": "^4.0.1"
Expand All @@ -25,6 +26,7 @@
"getopts": "^2.2.4",
"glob": "^7.1.2",
"parse-link-header": "^1.0.1",
"puppeteer": "^3.3.0",
"strip-ansi": "^5.2.0",
"rxjs": "^6.5.3",
"tar-fs": "^1.16.3",
Expand Down
2 changes: 2 additions & 0 deletions packages/kbn-test/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ export { runFailedTestsReporterCli } from './failed_tests_reporter';
export { makeJunitReportPath } from './junit_report_path';

export { CI_PARALLEL_PROCESS_PREFIX } from './ci_parallel_process_prefix';

export * from './page_load_metrics';
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { ToolingLog } from '@kbn/dev-utils';
import { NavigationOptions, createUrl, navigateToApps } from './navigation';

export async function capturePageLoadMetrics(log: ToolingLog, options: NavigationOptions) {
const responsesByPageView = await navigateToApps(log, options);

const assetSizeMeasurements = new Map<string, number[]>();

const numberOfPagesVisited = responsesByPageView.size;

for (const [, frameResponses] of responsesByPageView) {
for (const [, { url, dataLength }] of frameResponses) {
if (url.length === 0) {
throw new Error('navigateToApps(); failed to identify the url of the request');
}
if (assetSizeMeasurements.has(url)) {
assetSizeMeasurements.set(url, [dataLength].concat(assetSizeMeasurements.get(url) || []));
} else {
assetSizeMeasurements.set(url, [dataLength]);
}
}
}

return Array.from(assetSizeMeasurements.entries())
.map(([url, measurements]) => {
const baseUrl = createUrl('/', options.appConfig.url);
const relativeUrl = url
// remove the baseUrl (expect the trailing slash) to make url relative
.replace(baseUrl.slice(0, -1), '')
// strip the build number from asset urls
.replace(/^\/\d+\//, '/');
return [relativeUrl, measurements] as const;
})
.filter(([url, measurements]) => {
if (measurements.length !== numberOfPagesVisited) {
// ignore urls seen only on some pages
return false;
}

if (url.startsWith('data:')) {
// ignore data urls since they are already counted by other assets
return false;
}

if (url.startsWith('/api/') || url.startsWith('/internal/')) {
// ignore api requests since they don't have deterministic sizes
return false;
}

const allMetricsAreEqual = measurements.every((x, i) =>
i === 0 ? true : x === measurements[i - 1]
);
if (!allMetricsAreEqual) {
throw new Error(`measurements for url [${url}] are not equal [${measurements.join(',')}]`);
}

return true;
})
.map(([url, measurements]) => {
return { group: 'page load asset size', id: url, value: measurements[0] };
});
}
90 changes: 90 additions & 0 deletions packages/kbn-test/src/page_load_metrics/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import Url from 'url';

import { run, createFlagError } from '@kbn/dev-utils';
import { resolve, basename } from 'path';
import { capturePageLoadMetrics } from './capture_page_load_metrics';

const defaultScreenshotsDir = resolve(__dirname, 'screenshots');

export function runPageLoadMetricsCli() {
run(
async ({ flags, log }) => {
const kibanaUrl = flags['kibana-url'];
if (!kibanaUrl || typeof kibanaUrl !== 'string') {
throw createFlagError('Expect --kibana-url to be a string');
}

const parsedUrl = Url.parse(kibanaUrl);

const [username, password] = parsedUrl.auth
? parsedUrl.auth.split(':')
: [flags.username, flags.password];

if (typeof username !== 'string' || typeof password !== 'string') {
throw createFlagError(
'Mising username and/or password, either specify in --kibana-url or pass --username and --password'
);
}

const headless = !flags.head;

const screenshotsDir = flags.screenshotsDir || defaultScreenshotsDir;

if (typeof screenshotsDir !== 'string' || screenshotsDir === basename(screenshotsDir)) {
throw createFlagError('Expect screenshotsDir to be valid path string');
}

const metrics = await capturePageLoadMetrics(log, {
headless,
appConfig: {
url: kibanaUrl,
username,
password,
},
screenshotsDir,
});
for (const metric of metrics) {
log.info(`${metric.id}: ${metric.value}`);
}
},
{
description: `Loads several pages with Puppeteer to capture the size of assets`,
flags: {
string: ['kibana-url', 'username', 'password', 'screenshotsDir'],
boolean: ['head'],
default: {
username: 'elastic',
password: 'changeme',
debug: true,
screenshotsDir: defaultScreenshotsDir,
},
help: `
--kibana-url Url for Kibana we should connect to, can include login info
--head Run puppeteer with graphical user interface
--username Set username, defaults to 'elastic'
--password Set password, defaults to 'changeme'
--screenshotsDir Set screenshots directory, defaults to '${defaultScreenshotsDir}'
`,
},
}
);
}
34 changes: 34 additions & 0 deletions packages/kbn-test/src/page_load_metrics/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export interface ResponseReceivedEvent {
frameId: string;
loaderId: string;
requestId: string;
response: Record<string, any>;
timestamp: number;
type: string;
}

export interface DataReceivedEvent {
encodedDataLength: number;
dataLength: number;
requestId: string;
timestamp: number;
}
21 changes: 21 additions & 0 deletions packages/kbn-test/src/page_load_metrics/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export * from './cli';
export { capturePageLoadMetrics } from './capture_page_load_metrics';
Loading

0 comments on commit 6f57fa0

Please sign in to comment.