Skip to content

feat: ddp-client connection liveness check and forceReopen#41353

Open
Rohit3523 wants to merge 5 commits into
developfrom
feat/ddp-connection-liveness
Open

feat: ddp-client connection liveness check and forceReopen#41353
Rohit3523 wants to merge 5 commits into
developfrom
feat/ddp-connection-liveness

Conversation

@Rohit3523

@Rohit3523 Rohit3523 commented Jul 13, 2026

Copy link
Copy Markdown
Member

Why this is needed

On mobile, a WebSocket can become a zombie: the client still reports status === 'connected', but the underlying TCP transport is already dead. Common causes:

  • Carrier/NAT bindings silently drop idle connections after a few minutes.
  • The OS closes sockets when the app is backgrounded; on wake the socket object looks alive but sends go nowhere.
  • These drops frequently never fire onclose, so the client has no signal that anything is wrong.

Symptom: the app looks connected, but method calls and subscriptions silently hang or fail, and the user only recovers by force-killing the app. The existing client only learns about death via onclose or the server's own ping timeout — neither is reliable when the drop is silent on the client side.

DDP already has ping/pong, but it's normally server-initiated (server pings, client answers). This PR adds a client-initiated liveness probe so the client can detect a dead path itself and recover autonomously.

Proposed changes

  • Connection.probe(timeoutMs?) — sends a DDP ping and resolves true if a pong arrives within timeoutMs.
  • Connection.forceReopen() — closes + reconnects, deduplicating concurrent callers via a shared in-flight promise.
  • Connection.checkAndReopen(probeTimeoutMs?) — probes when connected; force-reopens when not connected or on a dead probe. Also exposed on DDPSDK (sdk.checkAndReopen()).
  • Connection.close() now severs socket handlers before closing so a dying socket can't deliver late messages or fire onclose on the live connection.

Steps to test or reproduce

  • await sdk.checkAndReopen() returns true when the socket is healthy, and recovers when the transport is dead.
  • A zombie socket (no onclose, no traffic) is detected by the pong timeout and force-reopened.

Further comments

The methods are inert until something calls checkAndReopen() — e.g. on app foreground or a NetInfo connectivity change in the mobile app. That trigger wiring lives in the mobile repo and is intentionally out of scope here; this PR only provides the capability and makes it reachable on the public DDPSDK.

https://rocketchat.atlassian.net/browse/CORE-2282

Summary by CodeRabbit

  • New Features
    • Added connection liveness probing using DDP ping/pong checks.
    • Introduced APIs to force a connection reopen and to automatically check-and-reopen when needed.
    • Exposed the check-and-reopen capability via the DDP SDK.
  • Bug Fixes
    • Improved recovery from “zombie” WebSocket states where the connection is marked alive but the transport is dead.
    • Hardened connection shutdown and handler detachment to prevent late/stale socket events from affecting active sessions.
    • Deduplicated concurrent forced reopen attempts to reduce race conditions.
  • Tests
    • Added coverage for liveness probing, reopen behavior, and concurrency handling.

@CLAassistant

CLAassistant commented Jul 13, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a7406c44-f173-495c-b598-a48337579752

📥 Commits

Reviewing files that changed from the base of the PR and between 646cb64 and 42824ef.

📒 Files selected for processing (2)
  • packages/ddp-client/__tests__/Connection.spec.ts
  • packages/ddp-client/src/Connection.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ddp-client/src/Connection.ts
  • packages/ddp-client/tests/Connection.spec.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: CodeQL-Build
  • GitHub Check: Hacktron Security Check
  • GitHub Check: CodeQL-Build

Walkthrough

The DDP client adds ping/pong liveness probing, deduplicated reopening, safer socket closure, public recovery APIs, and tests for alive, dead, and disconnected states.

Changes

DDP liveness recovery

Layer / File(s) Summary
Public liveness API
packages/ddp-client/src/Connection.ts, packages/ddp-client/src/DDPSDK.ts, .changeset/fix-ddp-connection-liveness.md
Adds probe, forceReopen, and checkAndReopen to the public APIs and documents the patch release.
Socket probing and closure
packages/ddp-client/src/Connection.ts, packages/ddp-client/__tests__/Connection.spec.ts
Implements DDP ping/pong probing with timeout cleanup and detaches socket handlers before closing; tests cover successful and timed-out probes.
Reopen coordination
packages/ddp-client/src/Connection.ts, packages/ddp-client/__tests__/Connection.spec.ts
Deduplicates concurrent reopen calls and conditionally reopens dead or disconnected connections; tests cover recovery and session preservation.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Connection
  participant WebSocket
  Caller->>Connection: checkAndReopen()
  Connection->>WebSocket: send DDP ping
  WebSocket-->>Connection: pong or timeout
  Connection->>Connection: forceReopen when unavailable
  Connection-->>Caller: return recovery result
Loading

Suggested labels: type: feature

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main ddp-client liveness and reopen recovery changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • CORE-2282: Request failed with status code 401

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.

@changeset-bot

changeset-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 42824ef

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@rocket.chat/ddp-client Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dionisio-bot

dionisio-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@coderabbitai coderabbitai Bot added type: bug type: feature Pull requests that introduces new feature labels Jul 13, 2026
@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 68.69%. Comparing base (460858e) to head (42824ef).
⚠️ Report is 113 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #41353      +/-   ##
===========================================
- Coverage    69.12%   68.69%   -0.44%     
===========================================
  Files         3762     4139     +377     
  Lines       147936   159173   +11237     
  Branches     26428    28123    +1695     
===========================================
+ Hits        102261   109342    +7081     
- Misses       41185    44683    +3498     
- Partials      4490     5148     +658     
Flag Coverage Δ
e2e 58.81% <ø> (-0.41%) ⬇️
e2e-api 45.59% <ø> (-3.83%) ⬇️
unit 70.66% <92.30%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

@Rohit3523
Rohit3523 marked this pull request as ready for review July 25, 2026 15:54
@coderabbitai coderabbitai Bot removed the type: bug label Jul 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
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 `@packages/ddp-client/__tests__/Connection.spec.ts`:
- Around line 371-375: Wrap the fake-timer setup, probe execution, timer
advancement, and assertion in a try/finally block within the relevant Connection
test, and move jest.useRealTimers() into finally so timers are restored even
when the assertion fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7ad664a3-00a4-41d0-8187-b0c2a548e7bf

📥 Commits

Reviewing files that changed from the base of the PR and between 4f03dc4 and 9a1eb8d.

📒 Files selected for processing (2)
  • packages/ddp-client/__tests__/Connection.spec.ts
  • packages/ddp-client/src/Connection.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ddp-client/src/Connection.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Hacktron Security Check
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • packages/ddp-client/__tests__/Connection.spec.ts
**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • packages/ddp-client/__tests__/Connection.spec.ts
🧠 Learnings (5)
📚 Learning: 2026-02-24T19:22:48.358Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 38493
File: apps/meteor/tests/e2e/omnichannel/omnichannel-send-pdf-transcript.spec.ts:66-67
Timestamp: 2026-02-24T19:22:48.358Z
Learning: In Playwright end-to-end tests (e.g., under apps/meteor/tests/e2e/...), prefer locating elements by translated text (getByText) and ARIA roles (getByRole) over data-qa attributes. If translation values change, update the corresponding test locators accordingly. Never use data-qa locators. This guideline applies to all Playwright e2e test specs in the repository and helps keep tests robust to UI text changes and accessible semantics.

Applied to files:

  • packages/ddp-client/__tests__/Connection.spec.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • packages/ddp-client/__tests__/Connection.spec.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • packages/ddp-client/__tests__/Connection.spec.ts
📚 Learning: 2026-03-06T18:10:15.268Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39397
File: packages/gazzodown/src/code/CodeBlock.spec.tsx:47-68
Timestamp: 2026-03-06T18:10:15.268Z
Learning: In tests (especially those using testing-library/dom/jsdom) for Rocket.Chat components, the HTML <code> element has an implicit ARIA role of 'code'. Therefore, screen.getByRole('code') or screen.findByRole('code') will locate <code> elements even without a role attribute. Do not flag findByRole('code') as invalid in reviews; prefer using the implicit role instead of adding role="code" unless necessary for accessibility.

Applied to files:

  • packages/ddp-client/__tests__/Connection.spec.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • packages/ddp-client/__tests__/Connection.spec.ts
🔇 Additional comments (1)
packages/ddp-client/__tests__/Connection.spec.ts (1)

337-363: LGTM!

Also applies to: 378-459

Comment thread packages/ddp-client/__tests__/Connection.spec.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/ddp-client/src/Connection.ts Outdated
Comment thread packages/ddp-client/src/Connection.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature Pull requests that introduces new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants