node:net: publish diagnostics_channel events (net.client.socket, net.server.socket, net.server.listen) - #34209
node:net: publish diagnostics_channel events (net.client.socket, net.server.socket, net.server.listen)#34209robobun wants to merge 4 commits into
Conversation
….listen diagnostics channels
Node's built-in diagnostics_channel channels for net were never published,
so connection-level instrumentation (per-connection context via
channel.bindStore on net.client.socket, connection counting, socket
tagging) was silently blind under Bun.
Publish at the same points and with the same payloads as Node's lib/net.js:
- net.client.socket {socket} in Socket.prototype.connect
- net.server.socket {socket} in onconnection, after the 'connection' emit
- tracing:net.server.listen asyncStart {server, options} in
Server.prototype.listen after the already-listening check
- tracing:net.server.listen asyncEnd {server} in kRealListen after a
successful bind
- tracing:net.server.listen error {server, error} in listen's catch
Every publish is gated on the sub-channel's hasSubscribers so the common
no-subscriber path allocates nothing.
Adds Node's upstream test-diagnostics-channel-net.js to the parallel
suite and two subprocess tests in node-net.test.ts that pin the exact
payload shapes and ordering.
WalkthroughChangesNet diagnostics channel tracing
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
Comment |
|
Updated 12:30 AM PT - Jul 15th, 2026
❌ @robobun, your commit 4fca107 has 3 failures in
🧪 To try this PR locally: bunx bun-pr 34209That installs a local version of the PR into your bun-34209 --bun |
|
Status: all four documented net diagnostics channels now fire, payloads and ordering pinned against Node v26.3.0. Repro (fails on stock 1.4.0 with all 0s, passes with this branch): bun -e 'import dc from "node:diagnostics_channel"; import net from "node:net"; const G=["net.client.socket","net.server.socket","tracing:net.server.listen:asyncStart","tracing:net.server.listen:asyncEnd"]; const h=new Map(); for(const n of G)dc.subscribe(n,()=>h.set(n,(h.get(n)??0)+1)); const s=net.createServer(c=>c.end()); await new Promise(r=>s.listen(0,"127.0.0.1",r)); await new Promise(r=>{const c=net.connect(s.address().port,"127.0.0.1");c.on("close",r);c.on("error",r)}); s.close(); let bad=0; for(const n of G){const c=h.get(n)??0;console.log((c?" ok":"BUG"),n,"x"+c);if(!c)bad++} process.exit(bad?86:0)'CI on 4fca107 (build 73185): everything this PR adds or touches is green on every lane. The remaining reds are all
Ready for review. |
|
This PR may be a duplicate of:
🤖 Generated with Claude Code |
|
Overlaps the net.ts hunk of #32628 (draft, broader Node 26 diagnostics_channel sync across ~10 subsystems). This PR is the net-only slice and is ready to merge standalone; if #32628 lands first this becomes a no-op rebase. Two small differences from #32628's net.ts changes:
|
There was a problem hiding this comment.
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 `@src/js/node/net.ts`:
- Around line 3409-3412: Move the netServerListen.asyncStart.publish call in the
listen flow to immediately after argument normalization and the _handle check,
before validating ports, paths, or option objects. Preserve the existing
publication payload and subscriber guard so rejected listen attempts still
notify asyncStart subscribers in the expected order.
🪄 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: f4222060-6927-49d4-9521-cf12b3203a50
📒 Files selected for processing (3)
src/js/node/net.tstest/js/node/net/node-net.test.tstest/js/node/test/parallel/test-diagnostics-channel-net.js
Moves the ERR_SERVER_ALREADY_LISTEN check and the asyncStart publish to
the top of Server.prototype.listen, before port/path/option validation,
to match Node's event order: listen({port:-1}) publishes asyncStart
first and then throws ERR_SOCKET_BAD_PORT; listen() on an
already-listening server throws without publishing. A new subprocess
test pins both orderings against Node v26.3.0 output.
…bust under conservative GC
The napi common harness loads node:net at module init, so the new
require('node:diagnostics_channel') in net.ts shifts stack layout during
these tests' setup. JSC's conservative stack scan can then keep the
tracked object alive across a single gc() call. Wrap the object creation
in an IIFE and retry gc() up to 10 times, mirroring test_finalizer/test.js
in the same directory.
Same change as #32628; applied here so this PR is green standalone.
Co-authored-by: Ciro Spaciari <6379399+cirospaciari@users.noreply.github.com>
There was a problem hiding this comment.
No bugs found this pass, but deferring to a human since this touches Server.prototype.listen / Socket.prototype.connect hot paths and reorders the _handle check ahead of port validation (changes error precedence to match Node), plus bundles two unrelated NAPI finalizer-test tweaks.
What was reviewed:
- Publish sites match the
_http_client.ts/http2.ts/dgram.tspattern (hasSubscribers-gated, no allocation on the cold path). normalizeArgs(arguments)at the top oflisten()is pure and can't throw; the moved_handlecheck now matches Node's ordering and is pinned by the third subprocess test.- The cluster-worker
listenOnPrimaryHandlegap I raised earlier was shown to be Node-parity forcheckBindError; the remainingkRealListen-throws case is deferred to #34076. - The NAPI
test_function/test_instance_dataIIFE + gc-loop changes are the standard conservative-GC workaround and don't change what's asserted.
Extended reasoning...
Overview
Adds diagnostics_channel publishing to src/js/node/net.ts at five sites: net.client.socket in Socket.prototype.connect, net.server.socket in onconnection, and the tracing:net.server.listen asyncStart/asyncEnd/error sub-channels in Server.prototype.listen and kRealListen. Also moves the ERR_SERVER_ALREADY_LISTEN check to the top of listen() (before port/path validation) so asyncStart fires ahead of option validation, matching Node v26.3.0. Ships three subprocess tests in node-net.test.ts plus Node's upstream test-diagnostics-channel-net.js verbatim. Separately, two NAPI tests (test_function, test_instance_data) are wrapped in IIFEs with a 10× global.gc() loop to survive JSC's conservative stack scan.
Security risks
None. Diagnostics-channel publishes are observability-only; payloads are the socket/server objects the caller already holds. No new input parsing, no auth/crypto/permissions surface.
Level of scrutiny
Medium-high. The publish calls themselves are mechanical and mirror the existing pattern in _http_client.ts, http2.ts, and dgram.ts — that part I'd approve on its own. What warrants a human look is the _handle check reordering in Server.prototype.listen: it changes which error wins when an already-listening server is passed an invalid port (now ERR_SERVER_ALREADY_LISTEN instead of ERR_SOCKET_BAD_PORT). This is the Node-correct order and is snapshot-pinned, but it's a user-observable behavior change in a core module and worth a maintainer glance.
Other factors
- CodeRabbit's ordering concern was addressed in ffd2593 and confirmed resolved.
- My earlier note about the cluster-worker
listenOnPrimaryHandlepath not reachingnetServerListen.error.publishwas checked empirically against Node v26.3.0 by the author — Node also publishes onlyasyncStarton that path, so it's parity as-is; the residualkRealListen-throws-in-worker case is behind a pre-existing bug tracked by #34076. - The two NAPI test edits are unrelated to the PR title (CI-flake hardening for conservative GC). They're benign — same assertions, just IIFE-scoped so the tracked object's stack slot is off-frame before
gc()— but their presence is another reason a human should skim the diff. - Overlaps draft #32628; the author documented the two intentional deltas (per-sub-channel
hasSubscribersgating, and passingnormalizeArgs(arguments)[0]asoptions). - Tests use
expect(stderr).toBe(""), which the file's existing subprocess tests also do; finder agents flagged this and verifiers ruled it out.
Bun's
node:netnever published any of the built-in diagnostics channels, so connection-level instrumentation (per-connection context seeding viachannel.bindStoreonnet.client.socket, connection counting, socket tagging, OpenTelemetry's documented net instrumentation pattern) was silently blind under Bun.Repro
net.client.socketnet.server.sockettracing:net.server.listen:asyncStarttracing:net.server.listen:asyncEndFix
src/js/node/net.tsnow publishes at the same points and with the same payloads as Node'slib/net.js:net.client.socket{ socket }Socket.prototype.connect, after args are normalizednet.server.socket{ socket }onconnection, right afterself.emit('connection', socket)tracing:net.server.listen:asyncStart{ server, options }Server.prototype.listen, after theERR_SERVER_ALREADY_LISTENcheck;optionsisnormalizeArgs(arguments)[0], the same value Node passestracing:net.server.listen:asyncEnd{ server }kRealListen, after a successful bindtracing:net.server.listen:error{ server, error }listen's catch, with the formatted listen errorEvery publish is gated on the sub-channel's
hasSubscribersso the no-subscriber path allocates nothing, matching the existing pattern in_http_client.ts,http2.tsanddgram.ts.Verification
test/js/node/net/node-net.test.ts: two subprocess tests (isolated because dc subscriptions are process-global) pinning the exact payload shapes,instanceofchecks, and ordering for a successful listen (asyncStart→asyncEnd→ 3xclient.socket→ 3xserver.socket) and a failing listen (asyncStart→error EADDRINUSE, noasyncEnd).test/js/node/test/parallel/test-diagnostics-channel-net.js: Node's upstream test verbatim, which also covers all three client-socket entry points (net.connect,net.createConnection,new net.Socket().connect) andoptions.customOptionpassthrough onasyncStart.Both fail on
main(USE_SYSTEM_BUN=1) and pass with the change.node-net-server.test.tsand the existingtest-diagnostics-channel-*parallel tests are unchanged.no test proof · iteration 1 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/js/node/net/node-net.test.ts