Skip to content

Commit 1c83150

Browse files
[Jest] Lazy core mocks (elastic#235003)
Closes elastic#235079 Lazily instantiate core mocks, to relieve memory pressure. This has the benefit of tests using less memory usage, and thus us being able to run them in parallel without upsizing our CI workers, and CPU time spent on garbage collection goes down. Here's how it affects one of the alerting plugin test files, significant improvements across the board: ```stdout Benchmark diff: cwd -> f69f92 cwd: [Jest] Lazily instantiate mocks f69f92: [Agent Builder] Setup doclinks structure (elastic#235804) alerting-plugin-test - cwd f69f92 Δ CI Duration 6.3s ±1.7% 7.0s ±1.8% 711ms (+11.3%) 95%, +7.9%–+14.8% CPU Usage 6.5s ±2.0% 7.4s ±2.1% 852ms (+13.1%) 95%, +9.1%–+17.2% Max RSS 857.90 MB ±1.3% 977.20 MB ±2.4% 119.30 MB (+13.9%) 95%, +10.3%–+17.6% GC time 190ms ±4.4% 252ms ±3.4% 62ms (+32.6%) 95%, +23.8%–+42.0% ``` ### How it works Core mocks are instantiated using `lazyObject`. This is a helper utility that will be rewritten using a Babel plugin to create annotated getters that will only instantiate the mock when it is accessed. ### Why it helps Core mocks are eagerly created, and this means that many objects will be allocated. Specifically, jest will keep mocks in a global registry to support `jest.clearAllMocks()`. This means only when a test file completes these functions will be garbage collected. Increased memory consumption has two consequences that are relevant in this context: - they increase memory pressure, which means garbage collection has to run more frequently to prevent the process from running out of memory - our CI workers (that run Jest tests) have 16gb of memory. running jest configs in parallel means that there currently is a risk that either the processes themselves run out of memory, or the agent will. if we reduce memory usage, we can run them in parallel. --------- Co-authored-by: kibanamachine <[email protected]>
1 parent 6a2c0d2 commit 1c83150

File tree

208 files changed

+2068
-1270
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

208 files changed

+2068
-1270
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,6 +1796,7 @@ x-pack/platform/plugins/shared/ml/server/models/data_recognizer/modules/security
17961796
/.eslintrc.js @elastic/kibana-operations
17971797
/.eslintignore @elastic/kibana-operations
17981798
/.ci/.storybook @elastic/kibana-operations
1799+
/src/platform/packages/shared/kbn-lazy-object @elastic/kibana-operations @elastic/obs-ui-devex-team
17991800

18001801
# QA - Appex QA
18011802
.buildkite/scout_ci_config.yml @elastic/appex-qa

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,7 @@
15871587
"@kbn/json-ast": "link:packages/kbn-json-ast",
15881588
"@kbn/kbn-genai-cli": "link:x-pack/solutions/observability/packages/kbn-genai-cli",
15891589
"@kbn/kibana-manifest-schema": "link:packages/kbn-kibana-manifest-schema",
1590+
"@kbn/lazy-object": "link:src/platform/packages/shared/kbn-lazy-object",
15901591
"@kbn/lint-packages-cli": "link:packages/kbn-lint-packages-cli",
15911592
"@kbn/lint-ts-projects-cli": "link:packages/kbn-lint-ts-projects-cli",
15921593
"@kbn/managed-vscode-config": "link:packages/kbn-managed-vscode-config",

packages/kbn-babel-preset/common_preset.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ module.exports = (api) => ({
1414
// our explicit plugin configs to a sub-preset
1515
{
1616
plugins: [
17+
require.resolve('@kbn/lazy-object/src/plugin/lazy_babel_plugin'),
1718
require.resolve('babel-plugin-add-module-exports'),
18-
1919
// The class properties proposal was merged with the private fields proposal
2020
// into the "class fields" proposal. Babel doesn't support this combined
2121
// proposal yet, which includes private field, so this transform is

src/core/packages/analytics/browser-internal/src/analytics_service.test.mocks.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
import type { AnalyticsClient } from '@elastic/ebt/client';
1111
import { Subject } from 'rxjs';
12+
import { lazyObject } from '@kbn/lazy-object';
1213

13-
export const analyticsClientMock: jest.Mocked<AnalyticsClient> = {
14+
export const analyticsClientMock: jest.Mocked<AnalyticsClient> = lazyObject({
1415
optIn: jest.fn(),
1516
reportEvent: jest.fn(),
1617
registerEventType: jest.fn(),
@@ -20,7 +21,7 @@ export const analyticsClientMock: jest.Mocked<AnalyticsClient> = {
2021
telemetryCounter$: new Subject(),
2122
flush: jest.fn(),
2223
shutdown: jest.fn(),
23-
};
24+
});
2425

2526
jest.doMock('@elastic/ebt/client', () => ({
2627
createAnalytics: () => analyticsClientMock,

src/core/packages/analytics/browser-internal/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"@kbn/core-injected-metadata-browser-internal",
1212
"@kbn/core-analytics-browser",
1313
"@kbn/core-base-browser-mocks",
14-
"@kbn/core-injected-metadata-browser-mocks"
14+
"@kbn/core-injected-metadata-browser-mocks",
15+
"@kbn/lazy-object"
1516
],
1617
"exclude": ["target/**/*"]
1718
}

src/core/packages/analytics/browser-mocks/src/analytics_service.mock.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,36 @@ import { Subject } from 'rxjs';
1111
import type { PublicMethodsOf } from '@kbn/utility-types';
1212
import type { AnalyticsServiceSetup, AnalyticsServiceStart } from '@kbn/core-analytics-browser';
1313
import type { AnalyticsService } from '@kbn/core-analytics-browser-internal';
14+
import { lazyObject } from '@kbn/lazy-object';
1415

1516
type AnalyticsServiceContract = PublicMethodsOf<AnalyticsService>;
1617

1718
const createAnalyticsServiceSetup = (): jest.Mocked<AnalyticsServiceSetup> => {
18-
return {
19+
return lazyObject({
1920
optIn: jest.fn(),
2021
reportEvent: jest.fn(),
2122
registerEventType: jest.fn(),
2223
registerContextProvider: jest.fn(),
2324
removeContextProvider: jest.fn(),
2425
registerShipper: jest.fn(),
2526
telemetryCounter$: new Subject(),
26-
};
27+
});
2728
};
2829

2930
const createAnalyticsServiceStart = (): jest.Mocked<AnalyticsServiceStart> => {
30-
return {
31+
return lazyObject({
3132
optIn: jest.fn(),
3233
reportEvent: jest.fn(),
3334
telemetryCounter$: new Subject(),
34-
};
35+
});
3536
};
3637

3738
const createAnalyticsServiceMock = (): jest.Mocked<AnalyticsServiceContract> => {
38-
return {
39+
return lazyObject({
3940
setup: jest.fn().mockImplementation(createAnalyticsServiceSetup),
4041
start: jest.fn().mockImplementation(createAnalyticsServiceStart),
4142
stop: jest.fn(),
42-
};
43+
});
4344
};
4445

4546
export const analyticsServiceMock = {

src/core/packages/analytics/browser-mocks/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"kbn_references": [
1414
"@kbn/utility-types",
1515
"@kbn/core-analytics-browser",
16-
"@kbn/core-analytics-browser-internal"
16+
"@kbn/core-analytics-browser-internal",
17+
"@kbn/lazy-object"
1718
],
1819
"exclude": [
1920
"target/**/*",

src/core/packages/analytics/server-internal/src/analytics_service.test.mocks.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
import type { AnalyticsClient } from '@elastic/ebt/client';
1111
import { Subject } from 'rxjs';
12+
import { lazyObject } from '@kbn/lazy-object';
1213

13-
export const analyticsClientMock: jest.Mocked<AnalyticsClient> = {
14+
export const analyticsClientMock: jest.Mocked<AnalyticsClient> = lazyObject({
1415
optIn: jest.fn(),
1516
reportEvent: jest.fn(),
1617
registerEventType: jest.fn(),
@@ -20,7 +21,7 @@ export const analyticsClientMock: jest.Mocked<AnalyticsClient> = {
2021
telemetryCounter$: new Subject(),
2122
shutdown: jest.fn(),
2223
flush: jest.fn(),
23-
};
24+
});
2425

2526
jest.doMock('@elastic/ebt/client', () => ({
2627
createAnalytics: () => analyticsClientMock,

src/core/packages/analytics/server-internal/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"@kbn/core-base-server-internal",
1616
"@kbn/core-analytics-server",
1717
"@kbn/config-mocks",
18-
"@kbn/core-base-server-mocks"
18+
"@kbn/core-base-server-mocks",
19+
"@kbn/lazy-object"
1920
],
2021
"exclude": [
2122
"target/**/*",

src/core/packages/analytics/server-mocks/src/analytics_service.mock.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,48 +15,49 @@ import type {
1515
AnalyticsServicePreboot,
1616
} from '@kbn/core-analytics-server';
1717
import type { AnalyticsService } from '@kbn/core-analytics-server-internal';
18+
import { lazyObject } from '@kbn/lazy-object';
1819

1920
type AnalyticsServiceContract = PublicMethodsOf<AnalyticsService>;
2021

2122
const createAnalyticsServicePreboot = (): jest.Mocked<AnalyticsServicePreboot> => {
22-
return {
23+
return lazyObject({
2324
optIn: jest.fn(),
2425
reportEvent: jest.fn(),
2526
registerEventType: jest.fn(),
2627
registerContextProvider: jest.fn(),
2728
removeContextProvider: jest.fn(),
2829
registerShipper: jest.fn(),
2930
telemetryCounter$: new Subject(),
30-
};
31+
});
3132
};
3233

3334
const createAnalyticsServiceSetup = (): jest.Mocked<AnalyticsServiceSetup> => {
34-
return {
35+
return lazyObject({
3536
optIn: jest.fn(),
3637
reportEvent: jest.fn(),
3738
registerEventType: jest.fn(),
3839
registerContextProvider: jest.fn(),
3940
removeContextProvider: jest.fn(),
4041
registerShipper: jest.fn(),
4142
telemetryCounter$: new Subject(),
42-
};
43+
});
4344
};
4445

4546
const createAnalyticsServiceStart = (): jest.Mocked<AnalyticsServiceStart> => {
46-
return {
47+
return lazyObject({
4748
optIn: jest.fn(),
4849
reportEvent: jest.fn(),
4950
telemetryCounter$: new Subject(),
50-
};
51+
});
5152
};
5253

5354
const createAnalyticsServiceMock = (): jest.Mocked<AnalyticsServiceContract> => {
54-
return {
55+
return lazyObject({
5556
preboot: jest.fn().mockImplementation(createAnalyticsServicePreboot),
5657
setup: jest.fn().mockImplementation(createAnalyticsServiceSetup),
5758
start: jest.fn().mockImplementation(createAnalyticsServiceStart),
5859
stop: jest.fn(),
59-
};
60+
});
6061
};
6162

6263
export const analyticsServiceMock = {

0 commit comments

Comments
 (0)