| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
|
Review requested:
|
Sorry, something went wrong.
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #64991 +/- ##
==========================================
+ Coverage 90.27% 90.29% +0.01%
==========================================
Files 762 759 -3
Lines 247534 247624 +90
Branches 46694 46689 -5
==========================================
+ Hits 223457 223587 +130
+ Misses 15529 15512 -17
+ Partials 8548 8525 -23
... and 62 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Sorry, something went wrong.
When OutgoingMessage transitions from pre-socket buffering (Path B) to socket-connected writing (Path A), the backpressure domain changes — subsequent writes go directly to the socket, which enforces its own backpressure via socket.write() return values. The OM should emit drain at this transition point to signal that its buffer is clear and the caller can resume writing under the socket backpressure regime. Previously, _flush() gated drain emission on writableLength === 0 which included socket.writableLength. This conflated two independent backpressure domains: the OM pre-socket buffer and the socket kernel write queue. When the socket had a higher writableHighWaterMark than the OM (e.g. agent-reused socket from a prior request), the socket was never backpressured and never emitted drain, causing a permanent deadlock. Additionally, avoid reusing a pooled socket in http.Agent when its writableHighWaterMark differs from the request highWaterMark, so that the user backpressure threshold is respected for the common case of the built-in Agent. Signed-off-by: Naman Trivedi <trivenay@amazon.com> Fixes: nodejs#64680 Refs: nodejs#64653 Refs: nodejs#62936
Sorry, something went wrong.
There was a problem hiding this comment.
lgtm
Sorry, something went wrong.
Commit Queue failedIf this is your first time running this command, follow the instructions to create an access token. If you prefer to create it yourself on Github, see https://github.com/nodejs/node-core-utils/blob/main/README.md. Personal access token auth for Github.https://github.com/nodejs/node/actions/runs/31247780346 |
Sorry, something went wrong.
Commit Queue failed- Loading data for nodejs/node/pull/64991 ✔ Done loading data for nodejs/node/pull/64991 ----------------------------------- PR info ------------------------------------ Title http: emit drain on socket takeover and avoid stale HWM reuse (#64991) ⚠ Could not retrieve the email or name of the PR author's from user's GitHub profile! Branch trivenay:http-agent-hwm-no-reuse -> nodejs:main Labels http, author ready, needs-ci Commits 1 - http: emit drain on socket takeover and avoid stale HWM reuse Committers 1 - Naman Trivedi <trivenay@amazon.com> PR-URL: https://github.com/nodejs/node/pull/64991 Fixes: https://github.com/nodejs/node/issues/64680 Refs: https://github.com/nodejs/node/pull/64653 Refs: https://github.com/nodejs/node/pull/62936 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> ------------------------------ Generated metadata ------------------------------ PR-URL: https://github.com/nodejs/node/pull/64991 Fixes: https://github.com/nodejs/node/issues/64680 Refs: https://github.com/nodejs/node/pull/64653 Refs: https://github.com/nodejs/node/pull/62936 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> -------------------------------------------------------------------------------- ℹ This PR was created on Mon, 03 Aug 2026 22:22:17 GMT ✔ Approvals: 4 ✔ - Robert Nagy (@ronag) (TSC): https://github.com/nodejs/node/pull/64991#pullrequestreview-4876292155 ✔ - Trivikram Kamat (@trivikr): https://github.com/nodejs/node/pull/64991#pullrequestreview-4878185013 ✔ - James M Snell (@jasnell) (TSC): https://github.com/nodejs/node/pull/64991#pullrequestreview-4887938830 ✔ - Gürgün Dayıoğlu (@gurgunday): https://github.com/nodejs/node/pull/64991#pullrequestreview-4888441308 ✘ GitHub CI is still running ℹ Last Full PR CI on 2026-08-07T17:33:14Z: https://ci.nodejs.org/job/node-test-pull-request/75624/ - Querying data for job/node-test-pull-request/75624/ ✔ Build data downloaded ✔ Last Jenkins CI successful -------------------------------------------------------------------------------- ✔ Aborted `git node land` session in /home/runner/work/node/node/.ncuhttps://github.com/nodejs/node/actions/runs/31256774273 |
Sorry, something went wrong.
|
Looks like the commit-queue failure (✘ GitHub CI is still running) is likely caused by a stale GitHub Actions check suite (ID: 84102428609) from Aug 5 — stuck in queued with 0 check runs, never actually started. I pulled the check suites data locally and simulated the pr_checker.js logic — same result: that one orphaned suite blocks landing while 20 other suites from the same app completed successfully. For now, I can push an empty commit to get a fresh SHA to unblock this. If there's a way to delete/cancel that stale suite directly, that would work too. #64830 hit the same issue recently and was landed manually with git node land — happy to go whichever route makes sense here. On the fix side, if a check suite is still queued with 0 runs but a later run of the same app has already completed successfully, it should be ignored. If we think that's the right approach, I can raise a follow-up PR to nodejs/node-core-utils. |
Sorry, something went wrong.
Commit Queue failed- Loading data for nodejs/node/pull/64991 ✔ Done loading data for nodejs/node/pull/64991 ----------------------------------- PR info ------------------------------------ Title http: emit drain on socket takeover and avoid stale HWM reuse (#64991) ⚠ Could not retrieve the email or name of the PR author's from user's GitHub profile! Branch trivenay:http-agent-hwm-no-reuse -> nodejs:main Labels http, author ready, needs-ci Commits 1 - http: emit drain on socket takeover and avoid stale HWM reuse Committers 1 - Naman Trivedi <trivenay@amazon.com> PR-URL: https://github.com/nodejs/node/pull/64991 Fixes: https://github.com/nodejs/node/issues/64680 Refs: https://github.com/nodejs/node/pull/64653 Refs: https://github.com/nodejs/node/pull/62936 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> ------------------------------ Generated metadata ------------------------------ PR-URL: https://github.com/nodejs/node/pull/64991 Fixes: https://github.com/nodejs/node/issues/64680 Refs: https://github.com/nodejs/node/pull/64653 Refs: https://github.com/nodejs/node/pull/62936 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> -------------------------------------------------------------------------------- ℹ This PR was created on Mon, 03 Aug 2026 22:22:17 GMT ✔ Approvals: 4 ✔ - Robert Nagy (@ronag) (TSC): https://github.com/nodejs/node/pull/64991#pullrequestreview-4876292155 ✔ - Trivikram Kamat (@trivikr): https://github.com/nodejs/node/pull/64991#pullrequestreview-4878185013 ✔ - James M Snell (@jasnell) (TSC): https://github.com/nodejs/node/pull/64991#pullrequestreview-4887938830 ✔ - Gürgün Dayıoğlu (@gurgunday): https://github.com/nodejs/node/pull/64991#pullrequestreview-4888441308 ✘ GitHub CI is still running ℹ Last Full PR CI on 2026-08-08T12:19:17Z: https://ci.nodejs.org/job/node-test-pull-request/75624/ - Querying data for job/node-test-pull-request/75624/ ✔ Build data downloaded ✔ Last Jenkins CI successful -------------------------------------------------------------------------------- ✔ Aborted `git node land` session in /home/runner/work/node/node/.ncuhttps://github.com/nodejs/node/actions/runs/31291255089 |
Sorry, something went wrong.
When OutgoingMessage transitions from pre-socket buffering (Path B) to socket-connected writing (Path A), the backpressure domain changes — subsequent writes go directly to the socket, which enforces its own backpressure via socket.write() return values. The OM should emit drain at this transition point to signal that its buffer is clear and the caller can resume writing under the socket backpressure regime. Previously, _flush() gated drain emission on writableLength === 0 which included socket.writableLength. This conflated two independent backpressure domains: the OM pre-socket buffer and the socket kernel write queue. When the socket had a higher writableHighWaterMark than the OM (e.g. agent-reused socket from a prior request), the socket was never backpressured and never emitted drain, causing a permanent deadlock. Additionally, avoid reusing a pooled socket in http.Agent when its writableHighWaterMark differs from the request highWaterMark, so that the user backpressure threshold is respected for the common case of the built-in Agent. Signed-off-by: Naman Trivedi <trivenay@amazon.com> Fixes: #64680 Refs: #64653 Refs: #62936 PR-URL: #64991 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Can you post this bug report in https://github.com/nodejs/node-core-utils, and raise a PR? |
Sorry, something went wrong.
|
Filed the bug report and proposed fix at nodejs/node-core-utils#1160. Started a discussion there on the approach — once we align on the trade-offs, I can raise a PR for the fix. |
Sorry, something went wrong.
When OutgoingMessage transitions from pre-socket buffering (Path B) to socket-connected writing (Path A), the backpressure domain changes — subsequent writes go directly to the socket, which enforces its own backpressure via socket.write() return values. The OM should emit drain at this transition point to signal that its buffer is clear and the caller can resume writing under the socket backpressure regime. Previously, _flush() gated drain emission on writableLength === 0 which included socket.writableLength. This conflated two independent backpressure domains: the OM pre-socket buffer and the socket kernel write queue. When the socket had a higher writableHighWaterMark than the OM (e.g. agent-reused socket from a prior request), the socket was never backpressured and never emitted drain, causing a permanent deadlock. Additionally, avoid reusing a pooled socket in http.Agent when its writableHighWaterMark differs from the request highWaterMark, so that the user backpressure threshold is respected for the common case of the built-in Agent. Signed-off-by: Naman Trivedi <trivenay@amazon.com> Fixes: #64680 Refs: #64653 Refs: #62936 PR-URL: #64991 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
| Back | FazBrowse Home | New Git URL |
When OutgoingMessage transitions from pre-socket buffering (Path B) to socket-connected writing (Path A), the backpressure domain changes. The OM should emit drain at this transition to signal that its buffer is clear and the caller can resume writing under the socket's own backpressure.
Previously, _flush() gated drain emission on writableLength === 0 (which includes socket.writableLength). This conflated the OM's buffer state with the socket's kernel write queue. When the socket had a higher writableHighWaterMark than the OM (e.g., agent reuses a socket from a prior request with a different HWM), the socket was never backpressured, never emitted drain — permanent deadlock.
Approach
This PR makes two changes to address the problem:
1. Drain fix in _flush() (the must-have): Once _flushOutput() completes and all buffered data has been handed to the socket, emit drain unconditionally. From this point, the socket enforces its own backpressure via socket.write() return values. We don't wait for socket.writableLength to reach zero because that's the socket's backpressure domain — not the OM's. If the socket is full, the very next write() through Path A will return false and the user stops writing again naturally.
2. Agent HWM mismatch check (defense in depth): Don't reuse a pooled socket in http.Agent if its writableHighWaterMark differs from the request's highWaterMark. This ensures the user's backpressure threshold is respected for users of the built-in http.Agent. We chose to include this because highWaterMark on a connected TCP socket cannot be changed after creation (the underlying kernel buffer is not exposed via Node's TCP handle, and _writableState.highWaterMark is cosmetic since state.length stays 0 for connected sockets). Since there's no way to make a reused socket respect a different HWM, the most resilient approach is to not reuse it. For requests to the same host:port it's rare that different highWaterMark values are used, so socket reuse still happens for the vast majority of connections.
The drain fix alone prevents the deadlock universally (including custom agents and createConnection). The agent check additionally ensures correct backpressure behavior — not just absence of deadlock — for the common case.
Deadlock reproduction (requires reduced TCP send buffer)
Fixes: #64680
Refs: #64653
Refs: #62936