Skip to content

test(glob): cut leak.test.ts iterations ~3-10x by disabling ASAN quarantine - #36086

Open
robobun wants to merge 3 commits into
mainfrom
farm/fe865530/glob-leak-test-speedup
Open

test(glob): cut leak.test.ts iterations ~3-10x by disabling ASAN quarantine#36086
robobun wants to merge 3 commits into
mainfrom
farm/fe865530/glob-leak-test-speedup

Conversation

@robobun

@robobun robobun commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

test/js/bun/glob/leak.test.ts runs four RSS-growth probes, each spawning a subprocess that calls scan()/scanSync() 100 000 times. Under a debug+ASAN build that is ~60 s per subprocess, so the two async scan tests hit their own 60 s timeout and the file fails locally (it shows up as 11 s on the darwin-14-aarch64 release lane in build 83134).

Almost all of that work was spent saturating ASAN's freed-allocation quarantine so the 400 MB ASAN threshold would hold: the observed RSS delta plateaus at ~360 MB around 20-25 k iterations and the remaining 75-80 k buy nothing. Passing quarantine_size_mb=0 to the subprocess drops the ASAN noise floor to ~15 MB (~5-7 MB on release), which lets a single 30 MB bound separate "fixed" from "leaking" on every build variant.

Change

  • Reduce the measurement loop to 10 k iterations under ASAN/debug on Linux/Windows and 30 k otherwise. macOS keeps 30 k regardless of build flavor because PathBuffer is only 1 KB there, so a Box<GlobWalker> leak at 10 k would only reach ~13-26 MB locally. Warmup 1000 -> 500.
  • Set ASAN_OPTIONS=...:quarantine_size_mb=0 on the subprocess so the RSS delta measures the leak rather than the quarantine, and replace the per-build isASAN ? 400 : 100 threshold with a single 30 MB bound.
  • Pipe stdout/stderr and assert {stderr, growthMB, exitCode} plus growthMB < 30 instead of the bare expect(exitCode).toBe(0), so a failure reports the observed growth.
  • Fold the four near-identical bodies into one parameterised loop. Per-test ceiling stays at 60 s (the speedup comes from the iteration cut, not from lowering the ceiling).

Verification

Wall-clock before/after:

build before after
bun bd test (debug+ASAN, linux) 62 s (2 tests time out) 17 s
release linux 3.4 s 1.0 s
release windows 7.3 s 2.4 s

Leak detection is preserved: reintroducing the #29379 leak (forgetting the Box<GlobWalker> in __scan_sync / the path_buf box in WalkTask::then) makes all four tests fail with 65-89 MB against the 30 MB threshold.

(fail) leaks > scanSync does not leak GlobWalker struct  Received: 78.69
(fail) leaks > scanSync                                  Received: 88.89
(fail) leaks > scan does not leak GlobWalker struct      Received: 65.67
(fail) leaks > scan                                      Received: 64.70

Test-only change; no src/** diff.


no test proof · iteration 0 · Platform-specific test-only change; deferring to CI.

…antine

The four RSS-growth probes each ran 100k scan()/scanSync() iterations. Under
a debug+ASAN build that is ~60s per subprocess, so the async scan tests hit
their own 60s timeout and the file ran ~62s locally (11s on darwin release
CI, 7s on Windows release CI).

Most of that work was spent saturating ASAN's freed-allocation quarantine
(~360 MB plateau observed) so the 400 MB ASAN threshold would hold. Passing
quarantine_size_mb=0 to the subprocess drops the noise floor to ~15 MB under
ASAN and ~5-7 MB on release, which lets a single 30 MB bound separate the
fixed case from a reintroduced #29379 leak (65-90 MB at 10k iterations under
ASAN, >= 40 MB at 30k on darwin release where PathBuffer is 1 KB).

While here:
- pipe stdout/stderr and assert the growth number directly instead of only
  checking the exit code, so a failure reports the observed MB
- fold the four near-identical bodies into one parameterised loop
- halve the warmup and trim the per-test timeout to 30s
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

Glob leak tests

Layer / File(s) Summary
Scenario configuration
test/js/bun/glob/leak.test.ts
Defines platform-aware iterations, warmup count, a fixed 30 MB threshold, and four sync/async glob pattern cases.
Child-process RSS measurement
test/js/bun/glob/leak.test.ts
Runs each case in a child process, measures RSS growth after warmup and GC, disables ASAN quarantine, and asserts clean exit, empty stderr, and bounded growth.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: reducing glob leak-test iterations by disabling ASAN quarantine.
Description check ✅ Passed The description covers both required topics—what changed and how it was verified—though it uses different headings than the template.

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Status: test/js/bun/glob/leak.test.ts passes on every lane in build 83302. Self-review items are addressed (ab74dd7 keeps macOS at 30k; 65b3b14 restores the 60 s ceiling).

CI shows one hard failure, test/js/bun/http/serve.test.ts "releases a paused request body when the handler responds without reading it" on darwin 14 x64, which is the known flake tracked by #36088 and unrelated to this test-only diff. The remaining failures retried green.

Ready for review.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — test-only speedup that fixes debug+ASAN timeouts while preserving CI leak coverage. One inline nit about local macOS debug builds potentially slipping under the 30 MB bound at 10k iters; CI is unaffected so it's non-blocking.

Extended reasoning...

Overview

This PR rewrites test/js/bun/glob/leak.test.ts to run ~3-10× faster by disabling the ASAN quarantine (quarantine_size_mb=0) on the spawned subprocess, which collapses the RSS noise floor enough that a single 30 MB threshold works across build variants. Iteration counts drop from 100k to 10k (ASAN/debug) or 30k (release), the four copy-pasted test bodies are folded into a parameterised loop, and assertions now report the observed growth via a piped-stdout {stderr, growthMB, exitCode} object instead of a bare exit-code check. No src/** changes.

Security risks

None. Test-only change; the subprocess runs a fixed inline script under bunEnv in a tempDir, and the only new env var is a well-known ASAN tuning knob appended to any existing ASAN_OPTIONS.

Level of scrutiny

Low-to-moderate. The risk with leak-test tuning is silently weakening detection. The PR description shows the reintroduced #29379 leak still fails all four cases at 65-89 MB vs the 30 MB bound on the configurations that run in CI (Linux debug+ASAN, macOS/Linux/Windows release), so CI coverage is preserved. The inline nit flags that macOS local bun bd (debug, 1 KB PathBuffer, 10k iters) lands at ~13-26 MB — at or below the threshold — but that gap does not exist on any CI lane, so a regression cannot escape to main.

Other factors

The refactor follows harness conventions the repo enforces: {...bunEnv, ...} spread, concurrent pipe drain via Promise.all, combined-object assertion before the numeric bound, tempDir with using, and test.concurrent retained. The previous version of this file timed out under debug+ASAN (62 s vs 60 s limit), so this is strictly an improvement even accounting for the local-macOS nit. The ASAN_OPTIONS join with .filter(Boolean) correctly handles an unset base value. Approving with the nit left inline for the author to optionally address.

Comment thread test/js/bun/glob/leak.test.ts Outdated
macOS PathBuffer is 1 KB, so 10k iterations of the #29379 leak only reach
~13-26 MB locally and could slip under the 30 MB bound. Gate the 10k
reduction on !isMacOS so macOS keeps 30k regardless of build type; CI
(Linux ASAN, macOS/Windows release) is unchanged.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the macOS iteration-count nit from the previous review is addressed in ab74dd7 (&& !isMacOS), and no new issues turned up on re-review.

What was reviewed:

  • Confirmed the four original test names/patterns/fixtures are preserved 1:1 in the parameterised loop.
  • ASAN_OPTIONS composition via filter(Boolean).join(":") handles both set and unset bunEnv.ASAN_OPTIONS.
  • Checked the macOS-debug 30k-vs-30s-timeout concern: ruled out (release macOS did 100k in ~11s; 30k under debug+ASAN stays well inside 30s, and CI has no macOS debug lane anyway).
  • NaN from garbage stdout would still fail via toBeLessThan(30), so expect.any(Number) isn't a hole.
Extended reasoning...

Overview

Test-only refactor of test/js/bun/glob/leak.test.ts: cuts iteration counts 3-10× by disabling ASAN's freed-allocation quarantine on the subprocess (so RSS deltas measure the actual leak instead of quarantine saturation), collapses four copy-pasted test bodies into one parameterised for-loop over a cases table, replaces the branched isASAN ? 400 : 100 MB threshold with a single 30 MB bound, and improves failure diagnostics by piping stdout/stderr and asserting {stderr, growthMB, exitCode} before the threshold check. No src/** changes.

Delta since my previous review

I previously flagged that isASAN || isDebug ? 10_000 : 30_000 would give macOS debug/ASAN builds only 10k iterations, which at macOS's 1 KB PathBuffer would let a reintroduced #29379 leak (~13-26 MB) slip under the 30 MB threshold on local bun bd runs. Commit ab74dd7 addresses this exactly as suggested: (isASAN || isDebug) && !isMacOS ? 10_000 : 30_000, with the comment updated to explain why. Verified isMacOS is exported from test/harness.ts:20. The inline thread is resolved.

Security risks

None. Test-only; spawns bunExe() with a fixed inline script in a hermetic tempDir, no network, no untrusted input.

Level of scrutiny

Moderate — this is a leak-regression test being loosened (fewer iterations, tighter threshold, halved timeout), so the key question is whether detection sensitivity is preserved. The PR description demonstrates it empirically (reintroducing the #29379 leak fails all four tests at 65-89 MB vs the 30 MB bound), and the per-platform arithmetic checks out: Linux 10k × ≥4 KB, Windows 30k × ~96 KB, and now macOS 30k × ~1.3 KB all comfortably exceed 30 MB when leaking while the fixed noise floor (~7-15 MB with quarantine off) stays well below it. That satisfies REVIEW.md's "bound well below the unfixed leak" rule on every lane.

Other factors

  • Harness conventions are followed: using tempDir, await using proc, {...bunEnv, ...} spread, concurrent pipe drain via Promise.all, test.concurrent for independent subprocesses, stdout/stderr asserted before/alongside exitCode.
  • The one candidate raised this run (macOS debug/ASAN 30k iters vs the halved 30s timeout) was examined and ruled out: the old file ran 100k on macOS release in ~11s, so 30k on a debug+ASAN arm64 Mac lands well under 30s, and no CI lane runs macOS debug anyway — worst case is a slightly slower local run.
  • Number(stdout.trim()) on unexpected output yields NaN, which passes expect.any(Number) but fails toBeLessThan(30), so the assertion chain still fails closed.

macOS debug/ASAN now runs 30k iterations and its wall time there is
unmeasured; extrapolating from the 17s linux-debug@10k datapoint puts the
async pair near the previous 30s budget. The speedup in this PR comes from
the iteration cut and quarantine_size_mb=0, not from lowering the ceiling,
so restore the original 60s to avoid introducing a local macOS timeout.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the macOS iteration-count nit from my earlier pass is addressed in ab74dd7 (&& !isMacOS), and nothing else turned up on re-review.

What was reviewed

  • Verified all four original test names/patterns/fixtures are preserved and the parameterised loop is behaviour-equivalent to the four bodies it replaces.
  • Checked the ASAN_OPTIONS composition matches the repo idiom ([bunEnv.ASAN_OPTIONS, ...].filter(Boolean).join(":"), same as html-rewriter-leak / json5 / resolve-dns tests) and that pipes are drained concurrently via Promise.all.
  • Confirmed the NaN-on-crash path still fails: if the child dies before printing, exitCode: 0 / stderr: "" mismatch trips the first assertion, and NaN < 30 is false so toBeLessThan would also fail.
Extended reasoning...

Overview

Test-only rewrite of test/js/bun/glob/leak.test.ts. The four copy-pasted RSS-growth probes (100k iters, isASAN ? 400 : 100 MB threshold, stdio: inherit, bare expect(exitCode).toBe(0)) are folded into one parameterised for-of over a cases table. Iteration count drops to 10k on Linux/Windows debug/ASAN and 30k elsewhere; ASAN's freed-allocation quarantine is disabled on the subprocess so a single 30 MB threshold works across all build variants; stdout/stderr are now piped and the observed growth is asserted directly. No src/** changes.

Security risks

None. Pure test refactor; the only new external input is an ASAN_OPTIONS env var passed to a Bun subprocess the test itself spawns.

Level of scrutiny

Moderate for a test-only change, because the repo guidance is explicit that weakening a leak test's detection margin is a real regression. The two things that needed checking were (a) does the reduced iteration count + tighter threshold still catch the #29379 leak on every build/platform combination, and (b) does the refactor preserve the exact test names and inputs. The PR description shows empirical fail output (65-89 MB vs 30 MB) with the leak reintroduced on Linux debug+ASAN, and my earlier concern about the one uncovered combination (macOS debug/ASAN local at 10k iters × 1 KB PathBuffer ≈ 13-26 MB) was fixed in ab74dd7 by keeping macOS at 30k unconditionally.

Other factors

  • The change tightens the assertion surface relative to the old file: it now asserts stderr === "", exitCode === 0, and the numeric growth in a combined object (better failure diagnostics), where the old version only checked exit code with inherited stdio.
  • Subprocess handling follows REVIEW.md's rules: {...bunEnv, ...} spread, Promise.all([stdout.text(), stderr.text(), exited]) to avoid pipe-buffer deadlock, await using proc.
  • The ASAN_OPTIONS composition pattern is copied verbatim from several existing tests in the suite, so it's a known-good idiom rather than novel.
  • Harness imports (isDebug, isMacOS, isASAN) all exist in test/harness.ts.
  • Per-test 60s ceiling is retained (commit 65b3b14), so a genuine hang is still caught; the speedup comes purely from fewer iterations.

@robobun

robobun commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 5:43 AM PT - Jul 27th, 2026

@robobun, your commit 65b3b14 has 1 failures in Build #83302 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 36086

That installs a local version of the PR into your bun-36086 executable, so you can run:

bun-36086 --bun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant