Skip to content

fix: await in-flight requests from closing clients#2724

Merged
kettanaito merged 6 commits into
feat/3.0from
fix/in-flight-via-pending-responses
Apr 20, 2026
Merged

fix: await in-flight requests from closing clients#2724
kettanaito merged 6 commits into
feat/3.0from
fix/in-flight-via-pending-responses

Conversation

@kettanaito
Copy link
Copy Markdown
Member

@kettanaito kettanaito commented Apr 20, 2026

Warning

This is a breaking change as it includes breaking changes to the worker.

Changes

The worker now tracks all pending response promises by client.id. Then, whenever a client closes, it awaits its pending responses to settle before sending back a close confirmation message. This fixes the race condition between client closure and in-flight requests that might still be pending.

Verification

pnpm test:browser test/browser/msw-api/setup-worker/stop/in-flight-request.test.ts --repeat-each=200

400 passed (1.4m)

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 20, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 057215ea-50de-4b1a-a86e-55be49a8bb3a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR converts the worker stop operation to be asynchronous, enabling proper synchronization of in-flight requests before shutdown. Changes span type definitions, the setup worker API, service worker implementation, worker channel events, and message handling to resolve requests being processed after worker.stop() is called.

Changes

Cohort / File(s) Summary
Type Definitions
src/browser/glossary.ts, src/browser/utils/workerChannel.ts
Updated StopHandler type to support both sync and async implementations. Added CLIENT_CLOSED event mapping to WorkerChannelEventMap, removed interceptedAt from IncomingWorkerRequest, and renamed outgoing event from CLIENT_CLOSED to CLIENT_CLOSE.
Setup Worker API
src/browser/setup-worker.ts
Made the stop() method async, returning Promise<void> instead of void, and awaits the network.disable() call.
Service Worker Implementation
src/browser/sources/service-worker-source.ts, src/mockServiceWorker.js
Converted ServiceWorkerSource.disable() to async with deferred promise awaiting CLIENT_CLOSED event. Added pendingRequests tracking per client, changed message handling from CLIENT_CLOSED to CLIENT_CLOSE, awaits settled pending requests before cleanup, and removed interceptedAt request tracking logic.
Tests
test/browser/msw-api/setup-worker/stop/in-flight-request.test.ts
Updated window type from SetupWorkerApi to SetupWorker and modified stop invocations to await the async operation.

Sequence Diagram

sequenceDiagram
    participant Client
    participant SetupWorker
    participant ServiceWorkerSource
    participant ServiceWorker
    Client->>SetupWorker: stop()
    activate SetupWorker
    SetupWorker->>ServiceWorkerSource: disable()
    activate ServiceWorkerSource
    ServiceWorkerSource->>ServiceWorker: postMessage({type: 'msw/worker:stop'})
    activate ServiceWorker
    ServiceWorker->>ServiceWorker: await Promise.allSettled(pendingRequests)
    deactivate ServiceWorker
    ServiceWorker->>ServiceWorkerSource: postMessage({type: 'CLIENT_CLOSED'})
    ServiceWorkerSource->>ServiceWorkerSource: DeferredPromise resolves
    deactivate ServiceWorkerSource
    SetupWorker->>Client: Promise resolves
    deactivate SetupWorker
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • #2715 — Modifies service worker lifecycle and communication code for stop/start teardown synchronization
  • #2650 — Introduces functions and types that are now being modified in this PR for async behavior
  • #2720 — Addresses client shutdown signaling and service worker client-close message handling

Suggested labels

release candidate

Poem

🐰 A worker that stops when it should,
No lingering requests left to brood,
With async hands and deferred care,
We wait for pending requests with flair,
Now CLIENT_CLOSE brings peace of mind! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes comprehensively address issue #2597 by making worker.stop() async, awaiting in-flight requests before shutdown, and removing the early-return logic that previously allowed post-stop requests to pass through.
Out of Scope Changes check ✅ Passed All changes directly support the objective of preventing post-stop request handling by tracking pending requests, awaiting their completion, and updating related type signatures and test cases.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title accurately summarizes the main change: making the stop handler async to await in-flight requests before closing clients, which directly addresses issue #2597.
Description check ✅ Passed The pull request description clearly describes the changes: tracking pending response promises by client ID and awaiting them before sending close confirmation, which directly addresses the race condition issue #2597.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/in-flight-via-pending-responses

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as resolved.

@kettanaito kettanaito changed the base branch from main to feat/3.0 April 20, 2026 16:46
@kettanaito kettanaito changed the title feat: await in-flight requests from closing clients fix: await in-flight requests from closing clients Apr 20, 2026
@kettanaito kettanaito added the BREAKING CHANGE Pull request introducing breaking changes. label Apr 20, 2026
@kettanaito kettanaito added this to the 3.0 milestone Apr 20, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 20, 2026

Open in StackBlitz

npm i https://pkg.pr.new/msw@2724

commit: bc9c026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/browser/sources/service-worker-source.ts (1)

186-199: Refresh the shutdown notes to match the new close protocol.

The implementation no longer has a #stoppedAt passthrough guard in #handleRequest, so these comments now describe behavior that was removed.

📝 Proposed comment update
     /**
      * `@note` Tell the Service Worker to drop this client from its active set
-     * so it stops forwarding REQUEST events here. `stoppedAt` still guards
-     * any requests the SW already forwarded before this message arrived.
+     * so it stops forwarding REQUEST events here. The worker acknowledges
+     * this only after any pending responses for this client have settled.
      */
     this.#channel.postMessage('CLIENT_CLOSE')
     await closedPromise
 
     /**
      * `@note` Do NOT reset `workerPromise` here. The channel must continue to
-     * resolve the currently registered SW so that any in-flight requests the
-     * worker forwards after `stop()` can be answered with `PASSTHROUGH` by
-     * `#handleRequest`. `#startWorker` swaps in a fresh deferred on re-enable.
+     * resolve the currently registered SW until the close handshake completes.
+     * `#startWorker` swaps in a fresh deferred on re-enable.
      */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/browser/sources/service-worker-source.ts` around lines 186 - 199, Update
the shutdown comments to remove references to the removed `#stoppedAt`
passthrough guard and instead describe the current close protocol: when
`this.#channel.postMessage('CLIENT_CLOSE')` is sent and we await
`closedPromise`, note that we still keep the existing `workerPromise`/resolved
service worker alive so `#handleRequest` can process any in-flight requests
forwarded by the SW, and that `#startWorker` will create a fresh deferred only
when re-enabling; also remove any mention of a `stoppedAt` guard and ensure the
comment accurately ties the behavior to `#channel.postMessage('CLIENT_CLOSE')`,
`closedPromise`, `workerPromise`, `#handleRequest`, and `#startWorker`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/browser/sources/service-worker-source.ts`:
- Around line 186-199: Update the shutdown comments to remove references to the
removed `#stoppedAt` passthrough guard and instead describe the current close
protocol: when `this.#channel.postMessage('CLIENT_CLOSE')` is sent and we await
`closedPromise`, note that we still keep the existing `workerPromise`/resolved
service worker alive so `#handleRequest` can process any in-flight requests
forwarded by the SW, and that `#startWorker` will create a fresh deferred only
when re-enabling; also remove any mention of a `stoppedAt` guard and ensure the
comment accurately ties the behavior to `#channel.postMessage('CLIENT_CLOSE')`,
`closedPromise`, `workerPromise`, `#handleRequest`, and `#startWorker`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e59a331e-cb73-4277-8d83-25d4a30fc735

📥 Commits

Reviewing files that changed from the base of the PR and between 45132f8 and bc9c026.

📒 Files selected for processing (6)
  • src/browser/sources/service-worker-source.ts
  • src/mockServiceWorker.js
  • test/browser/msw-api/setup-worker/start/handlers-reset.test.ts
  • test/browser/msw-api/setup-worker/start/start-after-stop.test.ts
  • test/browser/msw-api/setup-worker/stop/removes-all-listeners.test.ts
  • test/browser/msw-api/unregister.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/mockServiceWorker.js

@kettanaito kettanaito merged commit d31f517 into feat/3.0 Apr 20, 2026
15 checks passed
@kettanaito kettanaito deleted the fix/in-flight-via-pending-responses branch April 20, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BREAKING CHANGE Pull request introducing breaking changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MSW sometimes still handles requests after worker is stopped

1 participant