Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/8.0' into fix-cursor-theme-iss…
Browse files Browse the repository at this point in the history
…ues-8.0
  • Loading branch information
nickofthyme committed Nov 3, 2021
2 parents 9ac7ed6 + 04ad494 commit 3a17512
Show file tree
Hide file tree
Showing 80 changed files with 1,922 additions and 2,231 deletions.
2 changes: 0 additions & 2 deletions .buildkite/pipelines/performance/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ steps:
agents:
queue: ci-group-6
depends_on: build
concurrency: 50
concurrency_group: 'performance-test-group'

- wait: ~
continue_on_failure: true
Expand Down
2 changes: 2 additions & 0 deletions .buildkite/scripts/steps/functional/performance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ cat << EOF | buildkite-agent pipeline upload
steps:
- command: .buildkite/scripts/steps/functional/performance_sub.sh
parallelism: "$ITERATION_COUNT"
concurrency: 20
concurrency_group: 'performance-test-group'
EOF


Expand Down
47 changes: 47 additions & 0 deletions src/core/public/apm_resource_counter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/

export class CachedResourceObserver {
private loaded = {
networkOrDisk: 0,
memory: 0,
};
private observer?: PerformanceObserver;

constructor() {
if (!window.PerformanceObserver) return;

const cb = (entries: PerformanceObserverEntryList) => {
const e = entries.getEntries();
e.forEach((entry: Record<string, any>) => {
if (entry.initiatorType === 'script' || entry.initiatorType === 'link') {
// If the resource is fetched from a local cache, or if it is a cross-origin resource, this property returns zero.
// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/transferSize
if (entry.name.indexOf(window.location.host) > -1 && entry.transferSize === 0) {
this.loaded.memory++;
} else {
this.loaded.networkOrDisk++;
}
}
});
};
this.observer = new PerformanceObserver(cb);
this.observer.observe({
type: 'resource',
buffered: true,
});
}

public getCounts() {
return this.loaded;
}

public destroy() {
this.observer?.disconnect();
}
}
117 changes: 116 additions & 1 deletion src/core/public/apm_system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
*/

jest.mock('@elastic/apm-rum');
import type { DeeplyMockedKeys } from '@kbn/utility-types/jest';
import type { DeeplyMockedKeys, MockedKeys } from '@kbn/utility-types/jest';
import { init, apm } from '@elastic/apm-rum';
import { ApmSystem } from './apm_system';
import { Subject } from 'rxjs';
import { InternalApplicationStart } from './application/types';

const initMock = init as jest.Mocked<typeof init>;
const apmMock = apm as DeeplyMockedKeys<typeof apm>;
Expand Down Expand Up @@ -39,6 +41,119 @@ describe('ApmSystem', () => {
expect(apm.addLabels).toHaveBeenCalledWith({ alpha: 'one' });
});

describe('manages the page load transaction', () => {
it('does nothing if theres no transaction', async () => {
const apmSystem = new ApmSystem({ active: true });
const mockTransaction: MockedKeys<Transaction> = {
type: 'wrong',
// @ts-expect-error 2345
block: jest.fn(),
mark: jest.fn(),
};
apmMock.getCurrentTransaction.mockReturnValue(mockTransaction);
await apmSystem.setup();
expect(mockTransaction.mark).not.toHaveBeenCalled();
// @ts-expect-error 2345
expect(mockTransaction.block).not.toHaveBeenCalled();
});

it('blocks a page load transaction', async () => {
const apmSystem = new ApmSystem({ active: true });
const mockTransaction: MockedKeys<Transaction> = {
type: 'page-load',
// @ts-expect-error 2345
block: jest.fn(),
mark: jest.fn(),
};
apmMock.getCurrentTransaction.mockReturnValue(mockTransaction);
await apmSystem.setup();
expect(mockTransaction.mark).toHaveBeenCalledTimes(1);
expect(mockTransaction.mark).toHaveBeenCalledWith('apm-setup');
// @ts-expect-error 2345
expect(mockTransaction.block).toHaveBeenCalledTimes(1);
});

it('marks apm start', async () => {
const apmSystem = new ApmSystem({ active: true });
const currentAppId$ = new Subject<string>();
const mark = jest.fn();
const mockTransaction: MockedKeys<Transaction> = {
type: 'page-load',
mark,
// @ts-expect-error 2345
block: jest.fn(),
end: jest.fn(),
addLabels: jest.fn(),
};

apmMock.getCurrentTransaction.mockReturnValue(mockTransaction);
await apmSystem.setup();

mark.mockReset();

await apmSystem.start({
application: {
currentAppId$,
} as any as InternalApplicationStart,
});

expect(mark).toHaveBeenCalledWith('apm-start');
});

it('closes the page load transaction once', async () => {
const apmSystem = new ApmSystem({ active: true });
const currentAppId$ = new Subject<string>();
const mockTransaction: MockedKeys<Transaction> = {
type: 'page-load',
// @ts-expect-error 2345
block: jest.fn(),
mark: jest.fn(),
end: jest.fn(),
addLabels: jest.fn(),
};
apmMock.getCurrentTransaction.mockReturnValue(mockTransaction);
await apmSystem.setup();
await apmSystem.start({
application: {
currentAppId$,
} as any as InternalApplicationStart,
});
currentAppId$.next('myapp');

expect(mockTransaction.end).toHaveBeenCalledTimes(1);

currentAppId$.next('another-app');

expect(mockTransaction.end).toHaveBeenCalledTimes(1);
});

it('adds resource load labels', async () => {
const apmSystem = new ApmSystem({ active: true });
const currentAppId$ = new Subject<string>();
const mockTransaction: Transaction = {
type: 'page-load',
// @ts-expect-error 2345
block: jest.fn(),
mark: jest.fn(),
end: jest.fn(),
addLabels: jest.fn(),
};
apmMock.getCurrentTransaction.mockReturnValue(mockTransaction);
await apmSystem.setup();
await apmSystem.start({
application: {
currentAppId$,
} as any as InternalApplicationStart,
});
currentAppId$.next('myapp');

expect(mockTransaction.addLabels).toHaveBeenCalledWith({
'loaded-resources': 0,
'cached-resources': 0,
});
});
});

describe('http request normalization', () => {
let windowSpy: any;

Expand Down
63 changes: 44 additions & 19 deletions src/core/public/apm_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import type { ApmBase, AgentConfigOptions } from '@elastic/apm-rum';
import { modifyUrl } from '@kbn/std';
import { CachedResourceObserver } from './apm_resource_counter';
import type { InternalApplicationStart } from './application';

/** "GET protocol://hostname:port/pathname" */
Expand All @@ -31,17 +32,21 @@ interface StartDeps {
export class ApmSystem {
private readonly enabled: boolean;
private pageLoadTransaction?: Transaction;
private resourceObserver: CachedResourceObserver;
private apm?: ApmBase;
/**
* `apmConfig` would be populated with relevant APM RUM agent
* configuration if server is started with elastic.apm.* config.
*/
constructor(private readonly apmConfig?: ApmConfig, private readonly basePath = '') {
this.enabled = apmConfig != null && !!apmConfig.active;
this.resourceObserver = new CachedResourceObserver();
}

async setup() {
if (!this.enabled) return;
const { init, apm } = await import('@elastic/apm-rum');
this.apm = apm;
const { globalLabels, ...apmConfig } = this.apmConfig!;
if (globalLabels) {
apm.addLabels(globalLabels);
Expand All @@ -50,43 +55,63 @@ export class ApmSystem {
this.addHttpRequestNormalization(apm);

init(apmConfig);
this.pageLoadTransaction = apm.getCurrentTransaction();

// Keep the page load transaction open until all resources finished loading
if (this.pageLoadTransaction && this.pageLoadTransaction.type === 'page-load') {
// @ts-expect-error 2339
this.pageLoadTransaction.block(true);
this.pageLoadTransaction.mark('apm-setup');
}
// hold page load transaction blocks a transaction implicitly created by init.
this.holdPageLoadTransaction(apm);
}

async start(start?: StartDeps) {
if (!this.enabled || !start) return;

if (this.pageLoadTransaction && this.pageLoadTransaction.type === 'page-load') {
this.pageLoadTransaction.mark('apm-start');
}
this.markPageLoadStart();

/**
* Register listeners for navigation changes and capture them as
* route-change transactions after Kibana app is bootstrapped
*/
start.application.currentAppId$.subscribe((appId) => {
const apmInstance = (window as any).elasticApm;
if (appId && apmInstance && typeof apmInstance.startTransaction === 'function') {
// Close the page load transaction
if (this.pageLoadTransaction && this.pageLoadTransaction.type === 'page-load') {
this.pageLoadTransaction.end();
this.pageLoadTransaction = undefined;
}
apmInstance.startTransaction(`/app/${appId}`, 'route-change', {
if (appId && this.apm) {
this.closePageLoadTransaction();
this.apm.startTransaction(`/app/${appId}`, 'route-change', {
managed: true,
canReuse: true,
});
}
});
}

/* Hold the page load transaction open, until all resources actually finish loading */
private holdPageLoadTransaction(apm: ApmBase) {
const transaction = apm.getCurrentTransaction();

// Keep the page load transaction open until all resources finished loading
if (transaction && transaction.type === 'page-load') {
this.pageLoadTransaction = transaction;
// @ts-expect-error 2339 block is a private property of Transaction interface
this.pageLoadTransaction.block(true);
this.pageLoadTransaction.mark('apm-setup');
}
}

/* Close and clear the page load transaction */
private closePageLoadTransaction() {
if (this.pageLoadTransaction) {
const loadCounts = this.resourceObserver.getCounts();
this.pageLoadTransaction.addLabels({
'loaded-resources': loadCounts.networkOrDisk,
'cached-resources': loadCounts.memory,
});
this.resourceObserver.destroy();
this.pageLoadTransaction.end();
this.pageLoadTransaction = undefined;
}
}

private markPageLoadStart() {
if (this.pageLoadTransaction) {
this.pageLoadTransaction.mark('apm-start');
}
}

/**
* Adds an observer to the APM configuration for normalizing transactions of the 'http-request' type to remove the
* hostname, protocol, port, and base path. Allows for coorelating data cross different deployments.
Expand Down
1 change: 1 addition & 0 deletions src/plugins/charts/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export {
ColorMode,
LabelRotation,
defaultCountLabel,
MULTILAYER_TIME_AXIS_STYLE,
} from './static';

export { ColorSchemaParams, Labels, Style } from './types';
2 changes: 2 additions & 0 deletions src/plugins/charts/common/static/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ export {
} from './color_maps';

export { ColorMode, LabelRotation, defaultCountLabel } from './components';

export * from './styles';
9 changes: 9 additions & 0 deletions src/plugins/charts/common/static/styles/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/

export * from './multilayer_timeaxis';
26 changes: 26 additions & 0 deletions src/plugins/charts/common/static/styles/multilayer_timeaxis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/

import { Position, RecursivePartial, AxisStyle } from '@elastic/charts';

export const MULTILAYER_TIME_AXIS_STYLE: RecursivePartial<AxisStyle> = {
tickLabel: {
visible: true,
padding: 0,
rotation: 0,
alignment: {
vertical: Position.Bottom,
horizontal: Position.Left,
},
},
tickLine: {
visible: true,
size: 0.0001,
padding: 4,
},
};
1 change: 1 addition & 0 deletions src/plugins/charts/public/static/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
export * from './colors';
export * from './components';
export * from './utils';
export * from '../../common/static/styles';
Loading

0 comments on commit 3a17512

Please sign in to comment.