Skip to content

Commit 0f9b8e4

Browse files
rubennortemeta-codesync[bot]
authored andcommitted
Cap maxWorkers and bump heap to bound Fantom Metro memory (#56529)
Summary: Pull Request resolved: #56529 Fantom runs every test through a single shared Metro server (started in `globalSetup`). Jest's default `maxWorkers` is `numCpus - 1`, so on a high-core box (e.g. 176 CPUs) ~175 workers fire bundle requests at Metro concurrently. Each in-flight request makes Metro materialize a full dependency `Graph` (transformed modules, source maps, inverse-deps, file-watcher subscription), which is hundreds of MB. The per-test `DELETE` eviction added in D101652820 only releases that memory after the bundle response completes, so the simultaneous in-flight set still blows past the previous Node `--max-old-space-size=8192` ceiling in `scripts/fantom.sh` — the Metro process aborts with `FATAL ERROR: Ineffective mark-compacts near heap limit` after just a handful of test suites. Two coordinated changes that balance throughput and safety: - `scripts/fantom.sh`: bump the Node heap from 8 GB to 16 GB so we have headroom over the observed steady-state peak. - `private/react-native-fantom/config/jest.config.js`: cap `maxWorkers` at `min(numCpus - 1, 16)`. With 8 workers the heap peaked at ~3 GB (~400 MB / worker), so 16 workers fits comfortably under a 16 GB cap with ~40% headroom. This is intentionally a balance rather than a hard worker cap — bumping the heap alone would still leave 100+ in-flight graphs racing GC, and capping workers alone leaves throughput on the table on big machines. Changelog: [Internal] Reviewed By: andrewdacenko Differential Revision: D101791795 fbshipit-source-id: 035c7235c32303f7b7f1eb05698b2a5cba90edc9
1 parent b1a8913 commit 0f9b8e4

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

private/react-native-fantom/config/jest.config.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,32 @@
1111
'use strict';
1212

1313
const baseConfig = require('../../../jest.config');
14+
const os = require('os');
1415
const path = require('path');
1516

17+
// Every Fantom worker shares a single Metro server. With unbounded
18+
// parallelism, concurrent bundle builds pile up faster than the
19+
// per-test DELETE eviction can free their dependency graphs. Cap
20+
// concurrency so the number of in-flight graphs stays bounded on
21+
// high-core machines, while still using all available CPUs on smaller
22+
// ones. The cap pairs with the Node heap size set in scripts/fantom.sh
23+
// (16 GB) to leave comfortable headroom over the observed peak.
24+
function getNumCpus() /*: number */ {
25+
if (typeof os.availableParallelism === 'function') {
26+
return os.availableParallelism();
27+
}
28+
const cpus = os.cpus();
29+
return cpus != null ? cpus.length : 1;
30+
}
31+
32+
const FANTOM_MAX_WORKERS /*: number */ = Math.max(
33+
1,
34+
Math.min(getNumCpus() - 1, 16),
35+
);
36+
1637
module.exports = {
1738
displayName: 'fantom',
39+
maxWorkers: FANTOM_MAX_WORKERS,
1840
rootDir: path.resolve(__dirname, '../../..') /*:: as string */,
1941
roots: [
2042
'<rootDir>/packages/react-native',

scripts/fantom.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ else
1616
export FANTOM_FORCE_OSS_BUILD=1
1717
fi
1818

19-
export NODE_OPTIONS='--max-old-space-size=8192'
19+
# The Fantom Jest process hosts a single shared Metro server. Each test
20+
# triggers a fresh Metro bundle build whose dependency graph (transformed
21+
# modules, source maps, inverse-deps, file-watcher subscription) lives in
22+
# memory until the per-test DELETE evicts it. With the maxWorkers cap in
23+
# jest.config.js (~16 in-flight bundles at peak), 16 GB gives ~40% heap
24+
# headroom over the observed steady state of ~6 GB.
25+
export NODE_OPTIONS='--max-old-space-size=16384'
2026

2127
# Parse arguments to extract custom flags
2228
ARGS=()

0 commit comments

Comments
 (0)