Skip to content

chore(runway): cherry-pick fix(perps): connection-aware ensureReady() to fix stale cache on slow connections cp-7.67.0#26334

Merged
joaoloureirop merged 1 commit intorelease/7.67.0from
runway-cherry-pick-7.67.0-1771584002
Feb 24, 2026
Merged

chore(runway): cherry-pick fix(perps): connection-aware ensureReady() to fix stale cache on slow connections cp-7.67.0#26334
joaoloureirop merged 1 commit intorelease/7.67.0from
runway-cherry-pick-7.67.0-1771584002

Conversation

@runway-github
Copy link
Copy Markdown
Contributor

@runway-github runway-github bot commented Feb 20, 2026

Description

Fixes TAT-2597
and TAT-2598:
After the preload PR merged, slow connections caused StreamChannels to
exhaust 150 polling retries (30s) in ensureReady() and silently give
up, leaving users with stale REST cache and no live WebSocket data —
positions not appearing after trade, missing prices.

Root Cause: StreamChannel.ensureReady() used blind polling
(isReady every 200ms × 150 retries) with no awareness of WebSocket
connection state. On slow connections, the connection had not even
established yet, so polling burned through all retries before data could
arrive.

Fix:

  • PerpsConnectionManager.waitForConnection() — exposes init/reconnect
    promises so channels can await instead of blind-polling
  • StreamChannel.ensureReady() — detects isConnecting state and
    awaits the connection promise via awaitConnectionThenConnect()

Result: PriceStreamChannel retries dropped from 33 → 0 on device
after this fix.

Changelog

CHANGELOG entry: Fixed stale cache on slow connections where positions
and prices were not updating after a trade

Related issues

Fixes: TAT-2597,
TAT-2598

Manual testing steps

Feature: Perps live data on slow connections

  Scenario: user opens a trade on a slow connection
    Given the app is connected to a slow 3G network
    And the user has navigated to the Perps trading screen

    When user opens a new position
    Then the position appears immediately in the positions list
    And price stream connects without excessive retries

  Scenario: user recovers from network drop
    Given the user is viewing live perps positions
    And the network connection drops momentarily

    When the network connection is restored
    Then live WebSocket data resumes without stale cache

Screenshots/Recordings

Before

After

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the
    app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described
    in the ticket it closes and includes the necessary testing evidence such
    as recordings and or screenshots.

Note

Medium Risk
Changes connection/retry timing and sequencing for Perps WebSocket subscriptions; regressions could delay or prevent live data if the new await/sentinel logic misfires under edge cases (disconnects, rapid resubscribe).

Overview
Perps stream channels now become connection-aware: StreamChannel.ensureReady() detects PerpsConnectionManager's isConnecting state and waits on a new waitForConnection() promise, then retries connect() (with duplicate-wait suppression and polling fallback on rejection).

Retry scheduling was tightened by clearing deferConnect timers on fire and centralizing the retry delay via PERPS_CONSTANTS.ConnectRetryDelayMs; MarketDataChannel now uses this constant as well. Tests were expanded to cover the new await/deferral behavior and the new PerpsConnectionManager.waitForConnection() contract.

Written by Cursor Bugbot for commit 39e206b. This will update automatically on new commits. Configure here.

43cf1d8

… to fix stale cache on slow connections cp-7.67.0 (#26324)

## **Description**

Fixes [TAT-2597](https://consensys-mesh.atlassian.net/browse/TAT-2597)
and [TAT-2598](https://consensys-mesh.atlassian.net/browse/TAT-2598):
After the preload PR merged, slow connections caused StreamChannels to
exhaust 150 polling retries (30s) in `ensureReady()` and silently give
up, leaving users with stale REST cache and no live WebSocket data —
positions not appearing after trade, missing prices.

**Root Cause:** `StreamChannel.ensureReady()` used blind polling
(`isReady` every 200ms × 150 retries) with no awareness of WebSocket
connection state. On slow connections, the connection had not even
established yet, so polling burned through all retries before data could
arrive.

**Fix:**
- `PerpsConnectionManager.waitForConnection()` — exposes init/reconnect
promises so channels can `await` instead of blind-polling
- `StreamChannel.ensureReady()` — detects `isConnecting` state and
awaits the connection promise via `awaitConnectionThenConnect()`

**Result:** PriceStreamChannel retries dropped from **33 → 0** on device
after this fix.

## **Changelog**

CHANGELOG entry: Fixed stale cache on slow connections where positions
and prices were not updating after a trade

## **Related issues**

Fixes: [TAT-2597](https://consensys-mesh.atlassian.net/browse/TAT-2597),
[TAT-2598](https://consensys-mesh.atlassian.net/browse/TAT-2598)

## **Manual testing steps**

```gherkin
Feature: Perps live data on slow connections

  Scenario: user opens a trade on a slow connection
    Given the app is connected to a slow 3G network
    And the user has navigated to the Perps trading screen

    When user opens a new position
    Then the position appears immediately in the positions list
    And price stream connects without excessive retries

  Scenario: user recovers from network drop
    Given the user is viewing live perps positions
    And the network connection drops momentarily

    When the network connection is restored
    Then live WebSocket data resumes without stale cache
```

## **Screenshots/Recordings**

### **Before**

<!-- PriceStreamChannel: 33 retries before data appears, positions
missing on slow connections -->

### **After**

<!-- PriceStreamChannel: 0 retries, positions appear immediately -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

[TAT-2597]:
https://consensyssoftware.atlassian.net/browse/TAT-2597?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[TAT-2598]:
https://consensyssoftware.atlassian.net/browse/TAT-2598?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[TAT-2597]:
https://consensyssoftware.atlassian.net/browse/TAT-2597?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches Perps connection/stream reconnection timing and retry logic;
mistakes could cause missed subscriptions or delayed real-time updates,
though changes are localized and well-covered by tests.
> 
> **Overview**
> Prevents Perps stream channels from burning through retry polling on
slow connections by making `StreamChannel.ensureReady()` detect
`isConnecting` and wait on the connection manager before retrying
`connect()`.
> 
> Adds `PerpsConnectionManager.waitForConnection()` (awaits
init/reconnect promises, swallowing rejections) and introduces
`awaitConnectionThenConnect()` with a sentinel timer to avoid duplicate
awaits, plus a small `deferConnect()` timer cleanup fix and a shared
`PERPS_CONSTANTS.ConnectRetryDelayMs` constant.
> 
> Expands unit tests to cover the new await/guard behavior
(duplicate-await prevention, resolve/reject fallbacks) and updates
market-data channel tests to use the new retry delay constant.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
7e0d599. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@runway-github runway-github bot requested a review from a team as a code owner February 20, 2026 10:40
@github-actions
Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbot metamaskbot added the team-bots Bot team (for MetaMask Bot, Runway Bot, etc.) label Feb 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

⏭️ Smart E2E selection skipped - base branch is not main (base: release/7.67.0)

All E2E tests pre-selected.

View GitHub Actions results

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

);
this.deferConnect(PERPS_CONSTANTS.ConnectRetryDelayMs);
}
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Dead .catch() handler due to error-swallowing waitForConnection

Medium Severity

The .catch() handler in awaitConnectionThenConnect is unreachable dead code. waitForConnection() wraps both initPromise and pendingReconnectPromise in try-catch blocks that swallow all errors, so the returned promise always resolves — never rejects. The "connection failed, falling back to polling" log message will never appear, making connection failures harder to diagnose. The system still recovers because the .then() path calls connect() which re-enters ensureReady() and eventually reaches deferConnect(), but the explicit error-handling intent is silently bypassed.

Additional Locations (1)

Fix in Cursor Fix in Web

@joaoloureirop joaoloureirop added the skip-sonar-cloud Only used for bypassing sonar cloud when failures are not relevant to the changes. label Feb 23, 2026
@joaoloureirop joaoloureirop enabled auto-merge (squash) February 24, 2026 12:04
@sonarqubecloud
Copy link
Copy Markdown

@joaoloureirop joaoloureirop merged commit 8f1ce83 into release/7.67.0 Feb 24, 2026
263 of 293 checks passed
@joaoloureirop joaoloureirop deleted the runway-cherry-pick-7.67.0-1771584002 branch February 24, 2026 12:06
@github-actions github-actions bot locked and limited conversation to collaborators Feb 24, 2026
@metamaskbot metamaskbot added the release-7.67.0 Issue or pull request that will be included in release 7.67.0 label Feb 25, 2026
@metamaskbot
Copy link
Copy Markdown
Collaborator

No release label on PR. Adding release label release-7.67.0 on PR, as PR was cherry-picked in branch 7.67.0.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

claude-code-assisted release-7.67.0 Issue or pull request that will be included in release 7.67.0 size-M skip-sonar-cloud Only used for bypassing sonar cloud when failures are not relevant to the changes. team-bots Bot team (for MetaMask Bot, Runway Bot, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants