Skip to content

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

Description

@pacocartones

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions