Skip to content

Commit 12692b7

Browse files
committed
build(android): mitigate build OOM errors w/worker cap and added heap
Cap Gradle workers at min(physical_cpus, 6) to limit parallel heap pressure; 5GB daemon heap handles peak packageDebugAndroidTest load. Scales with hardware without overwhelming low-core CI machines.
1 parent f28bf07 commit 12692b7

6 files changed

Lines changed: 237 additions & 12 deletions

File tree

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#!/bin/bash
2+
# Reproduce intermittent Android packageDebugAndroidTest / IncrementalSplitterRunnable
3+
# failures under cold install + parallel host load (mirrors run-full-tests.sh phase 2–5).
4+
#
5+
# Usage:
6+
# ./scripts/repro-android-build-flake.sh [attempts]
7+
# ATTEMPTS=5 ./scripts/repro-android-build-flake.sh
8+
#
9+
# Logs:
10+
# /tmp/rnfb-android-flake-repro.log full output
11+
# /tmp/rnfb-android-flake-repro.summary pass/fail per attempt
12+
13+
set -u
14+
15+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
16+
cd "$REPO_ROOT"
17+
18+
ATTEMPTS="${1:-${ATTEMPTS:-5}}"
19+
LOG="${LOG:-/tmp/rnfb-android-flake-repro.log}"
20+
SUMMARY="${SUMMARY:-/tmp/rnfb-android-flake-repro.summary}"
21+
22+
: >"$LOG"
23+
: >"$SUMMARY"
24+
25+
log() {
26+
echo "$1" | tee -a "$LOG" "$SUMMARY"
27+
}
28+
29+
# Plain rm only — do not call yarn build:clean before node_modules exists (rimraf lives there).
30+
clean_workspace_artifacts() {
31+
log " stopping gradle daemons..."
32+
(cd tests/android && ./gradlew --stop >>"$LOG" 2>&1) || true
33+
34+
log " removing android/ios/macos build dirs..."
35+
rm -rf \
36+
tests/android/build \
37+
tests/android/app/build \
38+
tests/android/.gradle \
39+
tests/android/app/.cxx \
40+
tests/ios/build \
41+
tests/macos/build \
42+
tests/dist
43+
44+
log " removing all node_modules..."
45+
find . -name node_modules -type d -prune -exec rm -rf {} + 2>/dev/null || true
46+
}
47+
48+
cold_reset() {
49+
log "=== COLD RESET $(date -Iseconds) ==="
50+
clean_workspace_artifacts
51+
52+
log " yarn install (codegen)..."
53+
yarn install >>"$LOG" 2>&1 || return 1
54+
55+
log " gradlew clean (post-codegen)..."
56+
(cd tests/android && ./gradlew clean --no-build-cache >>"$LOG" 2>&1) || true
57+
58+
log " pod install (ios + macos)..."
59+
(yarn tests:ios:pod:install >>"$LOG" 2>&1 & yarn tests:macos:pod:install >>"$LOG" 2>&1 & wait) || return 1
60+
}
61+
62+
# Android build with maximum coldness; other jobs match run-full-tests.sh parallel block.
63+
run_android_build_cold() {
64+
(
65+
cd tests/android
66+
./gradlew-with-worker-cap.sh \
67+
assembleDebug assembleAndroidTest lintDebug \
68+
-DtestBuildType=debug \
69+
--warning-mode all \
70+
--stacktrace \
71+
--no-build-cache \
72+
--rerun-tasks
73+
) >>"$LOG" 2>&1
74+
}
75+
76+
parallel_verify() {
77+
log "=== PARALLEL VERIFY $(date -Iseconds) ==="
78+
local pids=()
79+
local failed=0
80+
81+
yarn tests:ios:build >>"$LOG" 2>&1 & pids+=($!)
82+
yarn tests:macos:build >>"$LOG" 2>&1 & pids+=($!)
83+
run_android_build_cold & pids+=($!)
84+
yarn compare:types >>"$LOG" 2>&1 & pids+=($!)
85+
yarn lint:js >>"$LOG" 2>&1 & pids+=($!)
86+
yarn lint:ios:check >>"$LOG" 2>&1 & pids+=($!)
87+
yarn lint:markdown >>"$LOG" 2>&1 & pids+=($!)
88+
yarn lint:spellcheck >>"$LOG" 2>&1 & pids+=($!)
89+
yarn tests:jest >>"$LOG" 2>&1 & pids+=($!)
90+
91+
for pid in "${pids[@]}"; do
92+
wait "$pid" || failed=1
93+
done
94+
95+
return "$failed"
96+
}
97+
98+
extract_failure_hints() {
99+
grep -nE \
100+
'IncrementalSplitter|packageDebugAndroidTest FAILED|Caused by|NoSuchFile|OutOfMemory|BUILD FAILED|Command failed' \
101+
"$LOG" | tail -40 | tee -a "$SUMMARY" || true
102+
}
103+
104+
pass_count=0
105+
fail_count=0
106+
107+
for i in $(seq 1 "$ATTEMPTS"); do
108+
log ""
109+
log "=== ATTEMPT $i/$ATTEMPTS $(date -Iseconds) ==="
110+
111+
if cold_reset && parallel_verify; then
112+
log "ATTEMPT $i: PASS $(date -Iseconds)"
113+
pass_count=$((pass_count + 1))
114+
else
115+
log "ATTEMPT $i: FAIL $(date -Iseconds)"
116+
fail_count=$((fail_count + 1))
117+
extract_failure_hints
118+
fi
119+
done
120+
121+
log ""
122+
log "=== SUMMARY: $pass_count passed, $fail_count failed (of $ATTEMPTS) ==="
123+
log "Full log: $LOG"

scripts/run-full-tests.sh

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
set -e
66
# Create temporary directory for logs
77
TMP_DIR=$(mktemp -d)
8+
echo "Step logs directory: $TMP_DIR"
89

910
# Clean up any stale metro bundler or firebase emulator processes
1011
function terminate_testing_processes() {
@@ -37,8 +38,8 @@ run_yarn_script() {
3738
# Run the yarn command and redirect output to log file
3839
if ! yarn "$script_name" > "$log_file" 2>&1; then
3940
echo "Command failed: yarn $script_name"
41+
echo "Full log preserved at: $log_file"
4042
cat "$log_file"
41-
rm -f "$log_file"
4243
return 1
4344
fi
4445

@@ -50,20 +51,35 @@ run_yarn_script() {
5051
run_yarn_scripts_parallel() {
5152
local scripts=("$@")
5253
local pids=()
53-
local pid failed=0
54+
local script_names=()
55+
local failed_scripts=()
56+
local i=0
57+
local failed=0
5458

5559
(
5660
trap 'kill 0' SIGINT
5761

5862
for script_name in "${scripts[@]}"; do
5963
run_yarn_script "$script_name" &
6064
pids+=($!)
65+
script_names+=("$script_name")
6166
done
6267

6368
for pid in "${pids[@]}"; do
64-
wait "$pid" || failed=1
69+
if ! wait "$pid"; then
70+
failed=1
71+
failed_scripts+=("${script_names[$i]}")
72+
fi
73+
i=$((i + 1))
6574
done
6675

76+
if [ "$failed" -ne 0 ]; then
77+
echo "Parallel step failed. Preserved logs in: $TMP_DIR"
78+
for script_name in "${failed_scripts[@]}"; do
79+
echo " - ${TMP_DIR}/${script_name}.log"
80+
done
81+
fi
82+
6783
exit "$failed"
6884
) || return 1
6985
}
@@ -72,13 +88,13 @@ echo "Starting full test execution..."
7288

7389
# 1. Dependency Installation
7490
echo "Installing dependencies..."
75-
run_yarn_script "install" || { echo "yarn install failed"; exit 1; }
91+
run_yarn_script "install" || { echo "yarn install failed. Logs preserved in: $TMP_DIR"; exit 1; }
7692

7793
echo "Installing iOS and macOS pods in parallel..."
7894
run_yarn_scripts_parallel \
7995
"tests:ios:pod:install" \
8096
"tests:macos:pod:install" \
81-
|| { echo "Pod install failed"; exit 1; }
97+
|| { echo "Pod install failed. Logs preserved in: $TMP_DIR"; exit 1; }
8298

8399
# 2–5. Builds, typechecks, lint, and unit tests (all parallel)
84100
echo "Running builds, typechecks, lint, and unit tests in parallel..."
@@ -92,7 +108,7 @@ run_yarn_scripts_parallel \
92108
"lint:markdown" \
93109
"lint:spellcheck" \
94110
"tests:jest" \
95-
|| { echo "Parallel verification failed"; exit 1; }
111+
|| { echo "Parallel verification failed. Logs preserved in: $TMP_DIR"; exit 1; }
96112

97113
# 6. E2E Tests with Flakiness Tolerance
98114
echo "Running E2E tests..."
@@ -114,10 +130,13 @@ sleep 30
114130
# Run E2E tests - 3 chances to succeed for flake tolerance
115131
for flavor in "ios" "android" "macos"; do
116132
for i in {1..3}; do
117-
echo "Running $flavor E2E test run attempt $i..."
118-
if ! yarn tests:"$flavor":test; then
133+
e2e_log="${TMP_DIR}/tests:${flavor}:test.attempt${i}.log"
134+
echo "Running $flavor E2E test run attempt $i... (log: $e2e_log)"
135+
if ! yarn tests:"$flavor":test > "$e2e_log" 2>&1; then
136+
echo "E2E attempt failed. Full log preserved at: $e2e_log"
137+
cat "$e2e_log"
119138
if [ $i -eq 3 ]; then
120-
echo "$flavor E2E test failed all $i attempts.";
139+
echo "$flavor E2E test failed all $i attempts. Logs preserved in: $TMP_DIR"
121140
terminate_testing_processes
122141
exit 1;
123142
fi

tests/.detoxrc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ module.exports = {
2727
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
2828
// keep in sync with android.debug.windows below, except gradlew vs gradlew.bat
2929
build:
30-
'cd android && ./gradlew assembleDebug assembleAndroidTest lintDebug -DtestBuildType=debug --warning-mode all && cd ..',
30+
'cd android && ./gradlew-with-worker-cap.sh assembleDebug assembleAndroidTest lintDebug -DtestBuildType=debug --warning-mode all --stacktrace && cd ..',
3131
reversePorts: [8080, 8081, 8090, 9000, 9099, 9199],
3232
},
3333
'android.debug.windows': {
3434
type: 'android.apk',
3535
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
3636
// android.debug.windows only exists to use .bat script vs shell here:
3737
build:
38-
'cd android && .\\gradlew.bat assembleDebug assembleAndroidTest lintDebug -DtestBuildType=debug --warning-mode all && cd ..',
38+
'cd android && .\\gradlew-with-worker-cap.bat assembleDebug assembleAndroidTest lintDebug -DtestBuildType=debug --warning-mode all --stacktrace && cd ..',
3939
reversePorts: [8080, 8081, 8090, 9000, 9099, 9199],
4040
},
4141
'android.release': {

tests/android/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ org.gradle.daemon=true
1111
org.gradle.caching=true
1212
org.gradle.parallel=true
1313
org.gradle.configureondemand=true
14-
org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8
14+
org.gradle.jvmargs=-Xmx5120M -Dfile.encoding=UTF-8
1515

1616
# When configured, Gradle will run in incubating parallel mode.
1717
# This option should only be used with decoupled projects. More details, visit
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@echo off
2+
setlocal EnableDelayedExpansion
3+
4+
if not defined GRADLE_WORKER_CAP set "GRADLE_WORKER_CAP=6"
5+
6+
set "CPUS="
7+
for /f %%i in ('wmic cpu get NumberOfCores /value ^| find "="') do (
8+
for /f "tokens=2 delims==" %%j in ("%%i") do set "CPUS=%%j"
9+
)
10+
11+
if not defined CPUS (
12+
if defined NUMBER_OF_PROCESSORS (
13+
set /a CPUS=NUMBER_OF_PROCESSORS / 2
14+
) else (
15+
set "CPUS=4"
16+
)
17+
)
18+
19+
if !CPUS! LSS 1 set "CPUS=1"
20+
21+
set "WORKERS=!CPUS!"
22+
if !WORKERS! GTR %GRADLE_WORKER_CAP% set "WORKERS=%GRADLE_WORKER_CAP%"
23+
24+
echo [gradlew-with-worker-cap] cpus=!CPUS! cap=%GRADLE_WORKER_CAP% -^> --max-workers=!WORKERS! 1>&2
25+
gradlew.bat --max-workers=!WORKERS! %*
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
WORKER_CAP="${GRADLE_WORKER_CAP:-6}"
5+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
6+
cd "$SCRIPT_DIR"
7+
8+
resolve_physical_cpus() {
9+
local cpus
10+
11+
case "$(uname -s)" in
12+
Darwin)
13+
if cpus="$(sysctl -n hw.physicalcpu 2>/dev/null)" && [[ "$cpus" =~ ^[0-9]+$ ]] && (( cpus >= 1 )); then
14+
echo "$cpus"
15+
return
16+
fi
17+
;;
18+
Linux)
19+
if command -v lscpu >/dev/null 2>&1; then
20+
cpus="$(lscpu -p=CORE 2>/dev/null | grep -Ev '^#' | sort -u | wc -l | tr -d ' ')"
21+
if [[ "$cpus" =~ ^[0-9]+$ ]] && (( cpus >= 1 )); then
22+
echo "$cpus"
23+
return
24+
fi
25+
local sockets cores_per_socket
26+
sockets="$(lscpu 2>/dev/null | awk '/^Socket\(s\)/ {print $2; exit}')"
27+
cores_per_socket="$(lscpu 2>/dev/null | awk '/^Core\(s\) per socket/ {print $4; exit}')"
28+
if [[ "$sockets" =~ ^[0-9]+$ && "$cores_per_socket" =~ ^[0-9]+$ ]] && (( sockets >= 1 && cores_per_socket >= 1 )); then
29+
echo $(( sockets * cores_per_socket ))
30+
return
31+
fi
32+
fi
33+
if [[ -r /proc/cpuinfo ]]; then
34+
cpus="$(awk '/^physical id/ {pid=$4} /^core id/ {cid=$4; k=pid","cid; if (!seen[k]++) n++} END {print n+0}' /proc/cpuinfo)"
35+
if [[ "$cpus" =~ ^[0-9]+$ ]] && (( cpus >= 1 )); then
36+
echo "$cpus"
37+
return
38+
fi
39+
fi
40+
;;
41+
esac
42+
43+
# Fallback: assume SMT — use half of logical CPUs
44+
local logical=4
45+
if command -v getconf >/dev/null 2>&1; then
46+
logical="$(getconf _NPROCESSORS_ONLN)"
47+
elif [[ -r /proc/cpuinfo ]]; then
48+
logical="$(grep -c ^processor /proc/cpuinfo)"
49+
fi
50+
echo $(( logical / 2 > 0 ? logical / 2 : 1 ))
51+
}
52+
53+
cpus="$(resolve_physical_cpus)"
54+
workers=$(( cpus < WORKER_CAP ? cpus : WORKER_CAP ))
55+
(( workers >= 1 )) || workers=1
56+
57+
echo "[gradlew-with-worker-cap] cpus=$cpus cap=$WORKER_CAP -> --max-workers=$workers" >&2
58+
exec ./gradlew --max-workers="$workers" "$@"

0 commit comments

Comments
 (0)