Summary
With interceptors.deduplicate(), aborting the first request of a coalesced group also rejects every other caller waiting on it — including callers that passed no signal at all.
I have a fix and tests, but it changes the interceptor's semantics rather than patching a slip, so I would rather agree the intended behaviour with you before sending a PR.
Reproduction
Two concurrent requests to the same resource through the deduplicate interceptor. The first carries an AbortSignal (a timeout, a cancelled UI action); the second carries none.
waiter outcome -> rejected: AbortError / This operation was aborted
The waiter had no signal of its own.
Where it comes from
DeduplicationHandler hands the primary caller the real dispatch controller (lib/handler/deduplication-handler.js:147-149). When that caller aborts, api-request.js:92 calls controller.abort(reason) on the real controller, the network request is cancelled, and onResponseError (:290-305) settles every waiting handler with AbortError.
The existing tests cover the inverse case — aborting a deduplicated request, from #5673 — but not this direction.
The question
For a request coalescer, I would expect the group to survive as long as any member still wants the response: the primary's abort should detach that caller, and the underlying request should only be cancelled once nobody is left waiting. Otherwise coalescing is not transparent, and whether your request succeeds depends on who else happened to ask first and whether they had a timeout.
If that is the intended semantics, I have it working locally: the primary gets a proxied controller (same shape as the existing #createWaitingHandler), abort settles only the primary, and the real dispatch is aborted only when the primary is gone and no waiting handler remains. Roughly 95 lines in the handler.
Two details that fell out of writing the tests:
- The
pendingRequests entry was not being released when every member of a group aborted, so a later request could join a request with no consumers left. That needed fixing too.
- Behaviour with no waiters is unchanged: the abort propagates exactly as it does today.
Tests: the new case fails on main with AbortError where a body was expected, and passes with the change. test/interceptors/deduplicate.js goes to 38/38; cache.js and retry.js stay at 103/103.
Happy to open the PR, or to take a different shape if you would rather the group be torn down with its primary — in which case the pendingRequests leak is still worth fixing on its own.
Summary
With
interceptors.deduplicate(), aborting the first request of a coalesced group also rejects every other caller waiting on it — including callers that passed nosignalat all.I have a fix and tests, but it changes the interceptor's semantics rather than patching a slip, so I would rather agree the intended behaviour with you before sending a PR.
Reproduction
Two concurrent requests to the same resource through the deduplicate interceptor. The first carries an
AbortSignal(a timeout, a cancelled UI action); the second carries none.The waiter had no signal of its own.
Where it comes from
DeduplicationHandlerhands the primary caller the real dispatch controller (lib/handler/deduplication-handler.js:147-149). When that caller aborts,api-request.js:92callscontroller.abort(reason)on the real controller, the network request is cancelled, andonResponseError(:290-305) settles every waiting handler withAbortError.The existing tests cover the inverse case — aborting a deduplicated request, from #5673 — but not this direction.
The question
For a request coalescer, I would expect the group to survive as long as any member still wants the response: the primary's abort should detach that caller, and the underlying request should only be cancelled once nobody is left waiting. Otherwise coalescing is not transparent, and whether your request succeeds depends on who else happened to ask first and whether they had a timeout.
If that is the intended semantics, I have it working locally: the primary gets a proxied controller (same shape as the existing
#createWaitingHandler),abortsettles only the primary, and the real dispatch is aborted only when the primary is gone and no waiting handler remains. Roughly 95 lines in the handler.Two details that fell out of writing the tests:
pendingRequestsentry was not being released when every member of a group aborted, so a later request could join a request with no consumers left. That needed fixing too.Tests: the new case fails on
mainwithAbortErrorwhere a body was expected, and passes with the change.test/interceptors/deduplicate.jsgoes to 38/38;cache.jsandretry.jsstay at 103/103.Happy to open the PR, or to take a different shape if you would rather the group be torn down with its primary — in which case the
pendingRequestsleak is still worth fixing on its own.