Skip to content

test: widen net-mongodb-pattern-leak RSS bound; await the named-pipe GC check - #33725

Open
robobun wants to merge 6 commits into
mainfrom
farm/609994f8/net-tcp-test-flakes
Open

test: widen net-mongodb-pattern-leak RSS bound; await the named-pipe GC check#33725
robobun wants to merge 6 commits into
mainfrom
farm/609994f8/net-tcp-test-flakes

Conversation

@robobun

@robobun robobun commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

net-mongodb-pattern-leak.test.ts: widen the release RSS bound

This test's expect(after2.rss - after1.rss).toBeLessThan(8 MB) has failed on roughly 40% of PR builds across linux (glibc and musl, x64 and aarch64) and macOS since build #67387, which is the WebKit upgrade in 00a93bd (#33133). It is always the tcp case, which runs first and so absorbs more warmup in round 2. A Buildkite annotation scan of ~150 affected builds puts the observed delta at 8.4-13.1 MB. Running five rounds locally shows heapStats().heapSize flat at ~2 MB and the Timeout/Promise/Function/AsyncGenerator counts flat, with later-round RSS deltas going negative on some runs: allocator segment and JIT code-page noise, not retention.

The precise regression guard for #12117 is the object-count assertions above the RSS line; the RSS check is documented as a backstop for native leaks heapStats cannot see. Widen the release bound to 24 MB (~2x the observed max) so a real per-iteration native leak at ITER=5000 (>= 5 KB/iter) still trips it.

node-net.test.ts: await the named-pipe GC check

The Windows-only should work with named pipes test called expectMaxObjectTypeCount(expect, "TCPSocket", before) without await (and read heapStats().objectTypeCounts.TLSSocket as its TCPSocket baseline), so the assertion floated and rejected ~1.5 s later inside whatever test happened to be running then. Every other call site in test/ awaits it.

Dropped from this PR

An earlier revision added an is_paused defer in packages/bun-usockets/src/loop.c to stop test-net-write-slow.js failing on the macOS arm64 lanes (kqueue EV_EOF dispatching on_end while on_data had paused the recv loop mid-drain). Three review rounds each found a new level-triggered busy-loop the defer introduces on epoll (paused+shut_down EPOLLHUP, AF_UNIX peer-close EPOLLHUP, partial-write-re-arms-READABLE + recv()==0), so the targeted approach is the wrong layer. #32257 addresses the whole class by making recv()==0 the sole eof source for readable polls on kqueue, which covers test-net-write-slow.js and every variant surfaced here; deferring that fix to #32257.

The Windows-only timeouts (test-net-connect-econnrefused.js, test-net-socket-reset-twice.js, node-net-server.test.ts 500 ms guards) and handle-leak.test.ts on alpine aarch64 (already addressed by #33622) are also separate.

Verification

USE_SYSTEM_BUN=1 bun test test/js/node/net/net-mongodb-pattern-leak.test.ts on linux x64: 10/10 pass at the new bound (was ~1/10 fail at 8 MB). The node-net.test.ts change is Windows-only and test-only.

…godb-pattern RSS bound

Two unrelated node:net test flakes that started in late June:

loop.c: #29831 added an is_paused check to the recv repeat-read, so on_data
pausing the socket (net.Socket push() returned false) now breaks out of the
recv loop with the kernel buffer undrained. kqueue's EV_EOF is set as soon as
the peer's FIN lands, so the immediately-following eof block dispatched end
with bytes still buffered; on resume the remainder arrived after push(null)
and node:net raised ERR_STREAM_PUSH_AFTER_EOF. test-net-write-slow.js on the
macOS arm64 lanes. Guard the eof block on !is_paused; us_socket_resume re-arms
readable and the level-triggered filter re-reports eof once recv() actually
returns 0.

net-mongodb-pattern-leak.test.ts: the tcp case's round-2 minus round-1 RSS
delta has been observed at 8-13 MB on release CI since the WebKit upgrade in
00a93bd, with heapSize and every object count flat, i.e. allocator and JIT
page noise rather than a leak. The precise regression guard for #12117 is the
object counts above it; the RSS line is a backstop for native leaks, so give
it ~2x headroom over the observed max.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR makes two independent test adjustments: it relaxes the RSS growth threshold in a MongoDB pattern leak regression test from 8 MiB to 24 MiB for non-ASAN/non-debug runs, and it corrects a named-pipe test to track TCPSocket object counts instead of TLSSocket, awaiting the assertion.

Changes

Leak Test RSS Threshold Adjustment

Layer / File(s) Summary
Adjust RSS bound in leak regression test
test/js/node/net/net-mongodb-pattern-leak.test.ts
Changes rssBound from 8 MiB to 24 MiB for non-ASAN/non-debug runs and updates the comment describing the expected RSS delta range.

Named-Pipe Heap Tracking Correction

Layer / File(s) Summary
Track TCPSocket counts and await assertion
test/js/node/net/node-net.test.ts
Changes the heap snapshot baseline from TLSSocket to TCPSocket object counts and adds await to the expectMaxObjectTypeCount call for TCPSocket.

Sequence Diagram(s)

Not applicable — these changes are test-only adjustments to thresholds and object-count tracking with no observable flow to diagram.

Estimated code review effort: Low

Suggested labels: test

Suggested reviewers: none

🐰 A hop, a nudge, a threshold grown,
from TLS to TCP counts we've flown,
RSS bounds now roomier still,
small test tweaks, no drama, no thrill.

🚥 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 names the two main changes: the RSS bound adjustment and the awaited named-pipe GC check.
Description check ✅ Passed It includes a detailed change summary and a verification section, covering the template’s required information.

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

@github-actions github-actions Bot added the claude label Jul 8, 2026
@robobun

robobun commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 4:53 AM PT - Jul 8th, 2026

@robobun, your commit 260fbfd has 3 failures in Build #70375 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 33725

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

bun-33725 --bun

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Found 2 issues this PR may fix:

  1. node:net: client data event never fires when write callback triggers end() against a loopback echo server #31383 - node:net client data event never fires when write callback triggers end() against a loopback echo server — same premature EOF dispatch on paused socket
  2. Readable.pipe(net.Socket) closes connection before peer response is delivered #32231 - Readable.pipe(net.Socket) closes connection before peer response is delivered — EOF dispatched before response bytes are read from kernel buffer

If this is helpful, copy the block below into the PR description to auto-close these issues on merge.

Fixes #31383
Fixes #32231

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. usockets: stop deriving eof from kqueue EV_EOF #32257 - Both fix the kqueue EV_EOF race condition where EOF is dispatched while the recv loop is paused mid-drain; usockets: stop deriving eof from kqueue EV_EOF #32257 takes a broader approach by stopping EV_EOF derivation entirely for polled sockets, while this PR adds a targeted !is_paused guard

🤖 Generated with Claude Code

@robobun

robobun commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

On the duplicate flag: #32257 is the comprehensive fix for the kqueue EV_EOF class (stops deriving eof from EV_EOF for any readable poll and covers the write-filter-only and busy-loop bail-out variants too). The loop.c change here is the minimal patch for the one path #29831 regressed: on_data pausing mid-drain with EV_EOF already set. The two do not conflict (different files), and once #32257 lands the !is_paused guard here becomes a no-op because eof is then only set by recv()==0, which never follows a dispatch in the same iteration. Happy to drop the loop.c hunk if #32257 is close; the RSS-bound change in net-mongodb-pattern-leak.test.ts is unrelated and stands on its own.

On the issue-finder's #31383 and #32231: #31383 is from 1.3.10 (Feb), predating #29831, and its payload is 5 bytes so push() never returns false and is_paused is never set; that one is the write-filter EV_EOF case #32257 covers and this PR does not. #32231's repro already prints received: pong on current main on Linux x64. Leaving both off the Fixes list.

Comment thread packages/bun-usockets/src/loop.c Outdated
Comment thread packages/bun-usockets/src/loop.c Outdated
Gating the whole eof block on !is_paused skipped us_socket_is_shut_down ->
close_raw, which on epoll is the only consumer of the level-triggered EPOLLHUP
a paused+shut-down socket registers at poll events==0, so pause();end();peer-FIN
busy-looped. Move the guard inside the block so only on_end is deferred. Adds a
Linux-reproducible test for that sequence.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/js/node/net/node-net.test.ts`:
- Around line 1006-1059: Both new socket-I/O tests are independent and use their
own dynamic TCP server/client setup, so they can run in parallel. Update the two
`it(...)` cases in `node-net.test.ts` to use `test.concurrent` (or the
equivalent concurrent form used elsewhere in this suite) so they don’t serialize
unnecessarily, while keeping the existing `createServer`, `connect`, and cleanup
logic unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 33290456-d7d4-443e-ba64-016c80730740

📥 Commits

Reviewing files that changed from the base of the PR and between 1de8bd3 and 8f1cb1c.

📒 Files selected for processing (3)
  • packages/bun-usockets/src/loop.c
  • test/js/node/net/net-mongodb-pattern-leak.test.ts
  • test/js/node/net/node-net.test.ts

Comment thread test/js/node/net/node-net.test.ts Outdated
… clients in the new tests

The Windows-only named-pipe test called expectMaxObjectTypeCount without
await (and read the TLSSocket count as its TCPSocket baseline), so the
assertion floated and fired ~1.5s later inside whatever test was running
then. Adding two tests at the end of the file put their sockets in scope
when it fired, which CI attributed to the last test. Also destroy the
client sockets in those two tests' finally blocks.
Comment thread packages/bun-usockets/src/loop.c Outdated
…ad READABLE)

Deferring for every is_paused busy-looped on epoll when a previously-paused
AF_UNIX socket's peer closed: unix_release_sock sets sk_shutdown=MASK on our
side so EPOLLHUP fires level-triggered, us_socket_is_shut_down is false (we
never called shutdown), and with events&READABLE==0 the recv loop never ran
so there are no deferred bytes. Gate the defer on events&READABLE so on_end
still fires for prior-paused sockets. Skip the EPOLLHUP tests on Windows
where uv_poll never passes eof from its callback.
Comment thread test/js/node/net/node-net.test.ts Outdated
Comment thread packages/bun-usockets/src/loop.c Outdated
…on_data's microtask drain

on_data's exit_scope drains microtasks before returning to loop.c, so a
queueMicrotask'd resume clears is_paused before the recv loop reads it and
the is_paused branch under test never runs. test-net-write-slow.js resumes
from setTimeout(20).
Comment thread packages/bun-usockets/src/loop.c Outdated
…d-pipe await fix

Three review rounds each found a new level-triggered busy-loop variant the
targeted is_paused defer introduces (paused+shut_down EPOLLHUP, AF_UNIX
peer-close EPOLLHUP, partial-write-rearms-READABLE + recv()==0). The class
is exactly what #32257 addresses by making recv()==0 the sole eof source for
readable polls on kqueue, so deferring the test-net-write-slow.js fix there.

This PR now only:
- widens the release RSS bound in net-mongodb-pattern-leak.test.ts from 8 to
  24 MB (observed 8-13 MB with flat heapSize and object counts since the
  WebKit upgrade in 00a93bd)
- awaits the floating expectMaxObjectTypeCount in the Windows named-pipe test
  and fixes its TLSSocket vs TCPSocket baseline, so its rejection stops being
  attributed to whatever test is running ~1.5s later
@robobun robobun changed the title usockets: defer eof dispatch when on_data paused mid-drain; widen mongodb-pattern RSS bound test: widen net-mongodb-pattern-leak RSS bound; await the named-pipe GC check Jul 8, 2026

@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 — with the loop.c change dropped, what remains is two straightforward test-only fixes.

Extended reasoning...

Overview

After three review rounds on the loop.c is_paused defer (each surfacing a new level-triggered busy-loop on epoll), the author dropped that change entirely in 260fbfd and deferred the whole kqueue EV_EOF class to #32257. What remains is two test files:

  • net-mongodb-pattern-leak.test.ts: widens the release-build RSS delta bound from 8 MB to 24 MB. The PR description documents that this bound has been failing ~40% of builds since the WebKit upgrade (#33133), with observed deltas of 8.4–13.1 MB while heapSize and object-type counts stay flat — allocator/JIT noise, not retention. The comment above the assertion already labels it a "weak backstop"; the precise #12117 regression guard is the object-count assertions above it, which are unchanged. 24 MB is ~2x the observed max and still trips a real 5 KB/iter native leak at ITER=5000. This matches CLAUDE.md's "RSS thresholds need ~2x headroom" guidance.
  • node-net.test.ts (Windows-only named-pipe test): fixes the baseline to read objectTypeCounts.TCPSocket instead of .TLSSocket (the assertion two lines below checks "TCPSocket", so the baseline was simply wrong), and adds the missing await before expectMaxObjectTypeCount — confirmed async at test/harness.ts:294, and every other call site in test/ awaits it. Without the await the assertion floated and rejected inside a later test.

Security risks

None. Test-only; no production code, no untrusted input handling, no auth/crypto/permissions.

Level of scrutiny

Low. Both changes are mechanical test-quality fixes with clear rationale. Widening an RSS bound is the kind of change that warrants scrutiny for whether it's papering over a real leak, but the PR description does that work: flat heapSize, flat object counts, later-round deltas going negative, and a specific attribution to the WebKit upgrade. The named-pipe fix is an obvious typo + missing-await that made an existing assertion vacuous.

Other factors

All my prior inline comments on this PR were on the now-dropped loop.c change and are resolved. The one CI failure on the current build (postgres-invalid-message-length.test.ts on Windows x64-baseline) is unrelated to either touched file. No outstanding reviewer comments.

@robobun

robobun commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

Build 70375 is 282 passed / 2 failed / 2 darwin-14-aarch64 still queued. The two failed jobs are unrelated infra:

  • :alpine: 3.23 aarch64: test/regression/issue/26030.test.ts (docker MySQL coordinator failed to start the service)
  • :windows: 2019 x64-baseline: postgres-invalid-message-length.test.ts (ERR_POSTGRES_CONNECTION_REFUSED)

Neither touches the two test files in this diff. net-mongodb-pattern-leak.test.ts and node-net.test.ts are green on every lane that ran them.

@robobun

robobun commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

Opened #34752 as an alternative: instead of widening the 8 MB bound, it runs one unmeasured warmup round so the RSS delta is taken at steady state (r3-r2 ranged -2.2..+2.0 MB over 30 runs vs -0.0..+11.0 MB for r2-r1). The extra RSS after #33133 is JIT code pages: heapSize is flat and BUN_JSC_useJIT=0 eliminates the delta. The node-net.test.ts await fix here is independent and still useful.

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