Skip to content

fix(h1): stop unref'ing the idle socket validation immediate - #5609

Open
marko1olo wants to merge 1 commit into
nodejs:mainfrom
marko1olo:fix/idle-socket-validation-unref
Open

fix(h1): stop unref'ing the idle socket validation immediate#5609
marko1olo wants to merge 1 commit into
nodejs:mainfrom
marko1olo:fix/idle-socket-validation-unref

Conversation

@marko1olo

Copy link
Copy Markdown
Contributor

This relates to...

Fixes #5600.

Rationale

Reusing an idle keep-alive socket stalls for up to a fast-timers tick before the request reaches the wire. With a pool of one connection against a 10 ms server, six sequential requests take over a second instead of the ~60 ms the server accounts for.

scheduleIdleSocketValidation moved from an unref'd setTimeout(..., 0) to setImmediate in #5499, and the unref?.() came along with it. An unref'd immediate does not hold the loop open, so on an otherwise idle loop the check phase is reached only once the next fast-timers tick wakes things up, and the queued request waits for that.

By the time the validation is scheduled, the request it exists for is already queued, so there is nothing to keep alive artificially. The immediate is a one-shot that runs on the very next check phase, and clearIdleSocketValidation cancels it when the socket goes away, so leaving it ref'd cannot delay process exit by more than that one turn — unlike the setTimeout this was inherited from.

Measured on Windows with Node 24, six sequential requests through a single-connection pool:

before   1095 ms, 1565 ms, 2045 ms   (per-request: 34, 41, 447, 66, 438, 50)
after     122 ms,  137 ms,  219 ms   (per-request: 38, 23, 15, 15, 16, 15)
server-side floor: 60 ms

Changes

  • lib/dispatcher/client-h1.js: drop the unref?.() on the idle socket validation immediate.
  • test/issue-5600.js: the reproduction from the issue, asserting the slowest of six sequential requests on a single-connection pool stays well under a fast-timers tick. It fails on current main with a slowest request around 425 ms.

npx borp --timeout 180000 --expose-gc -p "test/*.js" is 1439 passing here. The one failure in that run is test/http2-dispatcher.js "Should send http2 PING frames", which fails about two runs in three on unpatched main on this machine as well — unrelated to HTTP/1, and I have a separate branch for it if that is wanted.

Features

  • Implemented a new feature

Bug Fixes

  • Fixed a bug

Status

Reusing an idle keep-alive socket can stall for up to a fast-timers tick
(~500ms) before the request reaches the wire. With a pool of one connection
and a 10ms server, six sequential requests take over a second instead of
the ~60ms the server accounts for.

`scheduleIdleSocketValidation` moved from an unref'd `setTimeout(..., 0)` to
`setImmediate` in nodejs#5499, and the `unref?.()` came along with it. An unref'd
immediate does not hold the loop open, so on an otherwise idle loop the
check phase can be reached only once the next fast-timers tick wakes things
up, and the queued request waits for it. The request the validation exists
for is already queued at that point, so there is nothing to keep alive
artificially: the immediate is a one-shot that runs on the very next check
phase and `clearIdleSocketValidation` cancels it when the socket goes away.

test/issue-5600.js fails on the current code with a slowest request around
425ms and passes with the immediate left ref'd; on this machine the six
requests go from 1095/1565/2045ms across runs to 122/137/219ms.

Signed-off-by: marko1olo <marko1olo@users.noreply.github.com>
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.49%. Comparing base (8453a77) to head (7fd4337).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #5609   +/-   ##
=======================================
  Coverage   93.49%   93.49%           
=======================================
  Files         110      110           
  Lines       38281    38302   +21     
=======================================
+ Hits        35789    35811   +22     
+ Misses       2492     2491    -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@metcoder95
metcoder95 requested a review from mcollina July 30, 2026 08:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keep-alive connection reuse stalls up to ~500ms on an idle event loop (possible regression in 8.8.0)

2 participants