| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Implement suspension mechanism that keeps the port listener alive when a client misses ping-pong,
removes it from subscription/heartbeat state, and reactivates on next message from the resumed tab.
fix(shared-worker): `squashedChanges` iterates wrong array
Fix inner loop in `squashedChanges` to iterate `sortedChanges` instead of `requestAddChange` so that
add+remove pair elimination actually executes for same-request entries within aggregation window.
fix(shared-worker): orphaned `SubscriptionState` on empty changes
Guard `handleDelayedAggregation` against creating subscription state when changes queue is empty, and
add self-invalidation in `processChanges` when post-squash changes are empty with no active clients.
fix(shared-worker): `BFCache` interaction with `pagehide` listener
Remove `{ once: true }` from `pagehide` listener so the handler fires correctly when the page truly
navigates away after a prior BFCache entry consumed the one-shot listener.
fix(shared-worker): version comparison and token handling
Replace independent semver component comparison with hierarchical check, fix `accessTokensMap` being
overwritten on each new token, and serialize `onTokenChange` with a promise chain for ordering.
fix(tests): shared worker test reliability
Replace fixed-delay sequential subscription with channel-list verification on status events, and fix
`makeSendable` overrides to always return a valid tuple after calling `done()`.
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (6)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 1b8040bd-7b04-4b10-98c4-85e755a6775a 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:
WalkthroughThis PR implements client suspension and reactivation for PubNub's subscription worker, allowing clients to gracefully handle frozen or background-tab scenarios. When a client misses ping/pong, it is marked suspended; on the next message, it reactivates by clearing suspension state and reattaching event listeners. The middleware coordinates suspension detection and request cleanup via new worker events. ChangesClient Suspension and Reactivation
Sequence Diagram(s)sequenceDiagram
participant Client as PubNubClient
participant Manager as ClientsManager
participant Heartbeat as HeartbeatMgr
participant Subscribe as SubscribeMgr
participant Middleware as Middleware
Note over Client,Middleware: Client lifecycle: registration
Manager->>Client: create & register
Manager->>Heartbeat: ClientsManagerEvent.Registered
Heartbeat->>Heartbeat: attachClientListeners(client)
Manager->>Subscribe: ClientsManagerEvent.Registered
Subscribe->>Subscribe: attachClientListeners(client)
Note over Client,Middleware: Missed ping/pong: suspension
Manager->>Manager: timeout detected
Manager->>Client: suspendClient()
Client->>Client: _suspended = true
Client->>Client: cancelActiveRequests()
Manager->>Manager: dispatch UnregisterEvent
Note over Client,Middleware: Incoming message: reactivation
Client->>Client: message received while suspended
Client->>Client: handleReactivation()
Client->>Client: _suspended = false
Client->>Client: dispatch ReactivateEvent
Manager->>Client: reactivateClient()
Manager->>Heartbeat: ClientsManagerEvent.Reactivated
Heartbeat->>Heartbeat: attachClientListeners(client)
Manager->>Subscribe: ClientsManagerEvent.Reactivated
Subscribe->>Subscribe: attachClientListeners(client)
🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches 🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment @coderabbitai help to get the list of available commands and usage tips. |
Sorry, something went wrong.
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)src/transport/subscription-worker/subscription-worker-middleware.ts (1)src/transport/subscription-worker/components/pubnub-clients-manager.ts (1)440-445: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win
Scope suspended events to the target client.
At Line 444, shared-worker-client-suspended bypasses clientIdentifier filtering. Then Line 488 rejects all pending callbacks in this instance. A suspend event for another client can incorrectly fail this client’s in-flight requests.
🔧 Proposed fixif ( data.type !== 'shared-worker-ping' && data.type !== 'shared-worker-connected' && data.type !== 'shared-worker-console-log' && data.type !== 'shared-worker-console-dir' && - data.type !== 'shared-worker-client-suspended' && data.clientIdentifier !== this.configuration.clientIdentifier ) return;Also applies to: 481-498
🤖 Prompt for AI AgentsVerify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/transport/subscription-worker/subscription-worker-middleware.ts` around lines 440 - 445, The 'shared-worker-client-suspended' message is not being filtered by clientIdentifier, causing suspend events from other clients to reject this instance's in-flight requests; update the message handling so the condition that checks data.clientIdentifier !== this.configuration.clientIdentifier also applies to data.type === 'shared-worker-client-suspended', and modify the logic that rejects pending callbacks (the method that currently rejects all pending callbacks when handling 'shared-worker-client-suspended') to only reject callbacks associated with the suspended client's identifier (use the same clientIdentifier field on the pending callback entries) instead of rejecting every outstanding callback.211-214: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
Potential out-of-bounds splice when client not found in array.
If clientIdx is -1 (client not in array), splice(-1, 1) removes the last element, which would corrupt the client list.
Proposed fixconst clientsBySubscribeKey = this.clientBySubscribeKey[client.subKey]; if (clientsBySubscribeKey) { const clientIdx = clientsBySubscribeKey.indexOf(client); - clientsBySubscribeKey.splice(clientIdx, 1); + if (clientIdx >= 0) clientsBySubscribeKey.splice(clientIdx, 1);Note: suspendClient at line 155 already has this guard, but unregisterClient does not.
🤖 Prompt for AI AgentsVerify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/transport/subscription-worker/components/pubnub-clients-manager.ts` around lines 211 - 214, In unregisterClient (pubnub-clients-manager.ts) avoid calling splice with -1 by checking that the client exists before removing: locate the code that accesses this.clientBySubscribeKey[client.subKey] and computing clientIdx = clientsBySubscribeKey.indexOf(client) and add a guard like clientIdx !== -1 (or use a filter to remove the exact reference) before calling clientsBySubscribeKey.splice; mirror the same presence check used in suspendClient to prevent accidentally removing the last array element when the client isn't found.
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/transport/subscription-worker/components/heartbeat-requests-manager.ts`:
- Around line 183-186: The attachClientListeners method currently overwrites
this.clientAbortControllers[client.identifier] without aborting any existing
controller, which allows duplicate heartbeat/presence handlers; update
attachClientListeners to check this.clientAbortControllers[client.identifier]
and if an existing AbortController exists, call abort() (and optionally remove
it) before creating and storing the new AbortController for the PubNubClient,
and ensure any listener cleanup tied to the AbortSignal runs so duplicate
handlers are not left registered.
In `@src/transport/subscription-worker/components/subscribe-requests-manager.ts`:
- Around line 372-375: The attachClientListeners function currently overwrites
this.clientAbortControllers[client.identifier] with a new AbortController
without aborting any prior controller, which allows duplicate handlers to
remain; before creating and assigning a new AbortController in
attachClientListeners, check for an existing controller at
this.clientAbortControllers[client.identifier], call .abort() on it (and
optionally delete it) to cancel / clean up previous listeners/work, then create
and assign the new AbortController so re-attaching won’t leave duplicate
handlers enqueued.
In `@src/transport/subscription-worker/subscription-worker-middleware.ts`:
- Around line 232-248: The tokenChangeChain currently becomes permanently
rejected if parsedAccessToken or scheduleEventPost rejects; wrap the promise
sequence attached to tokenChangeChain so that errors are caught and handled
(e.g., log via the worker logger) and the chain returns a resolved value to
allow future updates to run; specifically, update the code that chains
parsedAccessToken(...) -> then(...) -> scheduleEventPost(updateEvent) on
tokenChangeChain to append a .catch(err => { /* log with context about
updateEvent/clientIdentifier */ }) that swallows or converts the error into a
resolved outcome so tokenChangeChain remains recoverable while still recording
the failure.
In `@test/integration/shared-worker/shared-worker.test.ts`:
- Line 1637: The test uses it.only which will cause all other tests to be
skipped; remove the .only from the it.only call (the test named "should receive
timeout presence events when browser tabs are closed") so it becomes a normal
it(...) test, ensuring the full suite runs in CI and eliminating this debugging
artifact.
---
Outside diff comments:
In `@src/transport/subscription-worker/components/pubnub-clients-manager.ts`:
- Around line 211-214: In unregisterClient (pubnub-clients-manager.ts) avoid
calling splice with -1 by checking that the client exists before removing:
locate the code that accesses this.clientBySubscribeKey[client.subKey] and
computing clientIdx = clientsBySubscribeKey.indexOf(client) and add a guard like
clientIdx !== -1 (or use a filter to remove the exact reference) before calling
clientsBySubscribeKey.splice; mirror the same presence check used in
suspendClient to prevent accidentally removing the last array element when the
client isn't found.
In `@src/transport/subscription-worker/subscription-worker-middleware.ts`:
- Around line 440-445: The 'shared-worker-client-suspended' message is not being
filtered by clientIdentifier, causing suspend events from other clients to
reject this instance's in-flight requests; update the message handling so the
condition that checks data.clientIdentifier !==
this.configuration.clientIdentifier also applies to data.type ===
'shared-worker-client-suspended', and modify the logic that rejects pending
callbacks (the method that currently rejects all pending callbacks when handling
'shared-worker-client-suspended') to only reject callbacks associated with the
suspended client's identifier (use the same clientIdentifier field on the
pending callback entries) instead of rejecting every outstanding callback.
Fix all unresolved CodeRabbit comments on this PR:
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: af3072b3-e398-4591-a9aa-2e0658bb5f06
📥 CommitsReviewing files that changed from the base of the PR and between 1d2ee43 and 4b07c09.
⛔ Files ignored due to path filters (4)
Sorry, something went wrong.
Add defensive guards for splice index, abort controller reuse, token chain recovery, and remove `it.only` from tests.
There was a problem hiding this comment.
src/transport/subscription-worker/components/pubnub-clients-manager.ts (1)🤖 Prompt for all review comments with AI agents178-194: 💤 Low value
Consider guarding against reactivating an already-active client.
reactivateClient doesn't check if the client is already registered in this.clients. If called for a non-suspended client (due to race conditions or programming error), it would overwrite the existing entry without aborting the previous AbortController, potentially leaking listeners.
🛡️ Suggested defensive guard🤖 Prompt for AI Agentsprivate reactivateClient(client: PubNubClient) { + // Abort any existing controller to prevent duplicate listeners if called erroneously. + const existing = this.clients[client.identifier]; + if (existing?.abortController) existing.abortController.abort(); + this.clients[client.identifier] = { client, abortController: new AbortController() };Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/transport/subscription-worker/components/pubnub-clients-manager.ts` around lines 178 - 194, The reactivateClient method can overwrite an existing this.clients[client.identifier] and leak the previous AbortController/listeners; update reactivateClient to first check if this.clients[client.identifier] already exists and either (a) if already active, return early (no-op) or (b) if you intend to replace, call abort() on the existing entry's abortController and clean up subscriptions before overwriting; ensure you also avoid duplicating entries in this.clientBySubscribeKey[client.subKey] when reactivating and only call subscribeOnClientEvents and dispatchEvent after safely replacing or confirming activation.
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Nitpick comments: In `@src/transport/subscription-worker/components/pubnub-clients-manager.ts`: - Around line 178-194: The reactivateClient method can overwrite an existing this.clients[client.identifier] and leak the previous AbortController/listeners; update reactivateClient to first check if this.clients[client.identifier] already exists and either (a) if already active, return early (no-op) or (b) if you intend to replace, call abort() on the existing entry's abortController and clean up subscriptions before overwriting; ensure you also avoid duplicating entries in this.clientBySubscribeKey[client.subKey] when reactivating and only call subscribeOnClientEvents and dispatchEvent after safely replacing or confirming activation.
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 62cc01f8-2c2e-46c4-9d68-59986c60ec51
📥 CommitsReviewing files that changed from the base of the PR and between 4b07c09 and 8d58d7c.
⛔ Files ignored due to path filters (4)
Sorry, something went wrong.
wondering about coderabbit's potential outdated/old abortController cleanup. |
Sorry, something went wrong.
Not an issue here — suspendClient already aborts the old controller and deletes the clients entry (lines 149-150) before reactivation can happen. So when reactivateClient runs, the slot is always empty. There's no previous entry to overwrite or leak. |
Sorry, something went wrong.
There was a problem hiding this comment.
LGTM
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
feat(shared-worker): client suspension and reactivation
Implement suspension mechanism that keeps the port listener alive when a client misses ping-pong, removes it from subscription/heartbeat state, and reactivates on next message from the resumed tab.
fix(shared-worker): squashedChanges iterates wrong array
Fix inner loop in squashedChanges to iterate sortedChanges instead of requestAddChange so that add+remove pair elimination actually executes for same-request entries within aggregation window.
fix(shared-worker): orphaned SubscriptionState on empty changes
Guard handleDelayedAggregation against creating subscription state when changes queue is empty, and add self-invalidation in processChanges when post-squash changes are empty with no active clients.
fix(shared-worker): BFCache interaction with pagehide listener
Remove { once: true } from pagehide listener so the handler fires correctly when the page truly navigates away after a prior BFCache entry consumed the one-shot listener.
fix(shared-worker): version comparison and token handling
Replace independent semver component comparison with hierarchical check, fix accessTokensMap being overwritten on each new token, and serialize onTokenChange with a promise chain for ordering.
fix(tests): shared worker test reliability
Replace fixed-delay sequential subscription with channel-list verification on status events, and fix makeSendable overrides to always return a valid tuple after calling done().