Skip to content

fix(deduplicate): detach the primary on abort instead of rejecting the group - #5765

Open
pacocartones wants to merge 1 commit into
nodejs:mainfrom
pacocartones:fix/5711-dedup-primary-abort-v2
Open

fix(deduplicate): detach the primary on abort instead of rejecting the group#5765
pacocartones wants to merge 1 commit into
nodejs:mainfrom
pacocartones:fix/5711-dedup-primary-abort-v2

Conversation

@pacocartones

Copy link
Copy Markdown
Contributor

Fixes #5711.

Problem

With interceptors.deduplicate(), aborting the primary request of a coalesced group rejected every waiter with AbortError — even waiters that passed no signal. The primary received the real dispatch controller, so its abort tore down the shared dispatch and rejected all members.

Fix

The primary now receives a proxied controller (mirroring the existing #createWaitingHandler). Aborting it detaches only the primary and defers to #maybeAbortRealDispatch, which tears down the shared dispatch only once no member (primary or waiter) is left waiting, releasing the pendingRequests entry via onComplete. Pause/resume still forward to the real controller, so backpressure is unchanged. With no waiters, abort propagates exactly as before.

Test

test/interceptors/deduplicate.js:

  • "aborting the primary request must not reject the coalesced waiters" — RED on main (waiter rejects with AbortError), GREEN with fix (waiter gets 200, origin hit once).
  • "when every member aborts, the pending entry is released" — guards the new teardown path.

deduplicate 38/38 + cache 88/88 + retry 16/16, npm run lint — all green.

@codecov-commenter

codecov-commenter commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.04132% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.53%. Comparing base (17117f6) to head (06dcbf1).
⚠️ Report is 14 commits behind head on main.

Files with missing lines Patch % Lines
lib/handler/deduplication-handler.js 95.04% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5765      +/-   ##
==========================================
+ Coverage   93.50%   93.53%   +0.02%     
==========================================
  Files         110      110              
  Lines       39072    39439     +367     
==========================================
+ Hits        36534    36888     +354     
- Misses       2538     2551      +13     

☔ 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.

@mcollina mcollina left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lgtm

Comment thread test/interceptors/deduplicate.js Outdated
acWaiter.abort()
strictEqual((await waiterOutcome)?.name, 'AbortError')

await sleep(50)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please do not use sleep, it's flaky.

Comment thread test/interceptors/deduplicate.js Outdated
const primaryOutcome = primary.then(() => 'resolved', err => err)
const waiterOutcome = waiter.then(() => 'resolved', err => err)

await sleep(50)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please do not use sleep, it's flaky.

Comment thread test/interceptors/deduplicate.js Outdated
const primaryOutcome = primary.then(() => 'resolved', err => err)

// Let the waiter join the coalesced group, then abort the primary.
await sleep(50)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please do not use sleep, it's flaky.

Comment thread test/interceptors/deduplicate.js Outdated
requestsToOrigin++
// Hold the response open long enough for the primary to abort while the
// waiter is still attached.
await sleep(300)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please do not use sleep, it's flaky.

…e group

Aborting the first request of a coalesced group cancelled the shared
dispatch, so every other caller waiting on it — including callers that
passed no signal — was rejected with an AbortError. Coalescing was not
transparent: whether your request succeeded depended on who asked first
and whether they had a timeout.

Hand the primary a proxied controller (same shape as the waiting
handlers). Aborting it now settles only the primary and detaches it from
the group; the underlying request is cancelled only once no member —
primary or waiting handler — is left waiting. This also releases the
pendingRequests entry when every member of a group aborts, so a later
request never joins a group with no consumers left. With no waiters the
abort still propagates exactly as before.

Refs: nodejs#5711
Signed-off-by: Paco Cartones <pacocartones@users.noreply.github.com>
@pacocartones
pacocartones force-pushed the fix/5711-dedup-primary-abort-v2 branch from 094431e to 06dcbf1 Compare September 4, 2026 15:16
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.

deduplicate: aborting the primary request rejects the other callers in the group

3 participants