Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/core-backend/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Improve WebSocket connection lifecycle tracing in `BackendWebSocketService` ([#7101](https://github.com/MetaMask/core/pull/7101))
- WebSocket connection duration is now properly reflected in trace span duration instead of only in custom data
- Trace all disconnections (both manual and unexpected) to provide complete connection lifecycle visibility in traces
- Omit `connectionDuration_ms` from disconnection traces when connection never established (onClose without onOpen)
- Update `BackendWebSocketService` default exponential backoff options for reconnection ([#7101](https://github.com/MetaMask/core/pull/7101))
- Increase default `reconnectDelay` from 500 milliseconds to 10 seconds
- Increase default `maxReconnectDelay` from 30 seconds to 60 seconds
- Simplify WebSocket disconnection code in `BackendWebSocketService` ([#7101](https://github.com/MetaMask/core/pull/7101))
- Centralize all disconnection logic in `ws.onclose` handler for single source of truth
- Centralize all state changes within `#establishConnection` method - state transitions only occur in `onopen` (CONNECTING → CONNECTED) and `onclose` (any state → DISCONNECTED)
- Add `MANUAL_DISCONNECT_CODE` (4999) and `MANUAL_DISCONNECT_REASON` constants to distinguish manual from unexpected disconnects

### Removed

- Remove `BackendWebSocketService Channel Message` trace as it provided no useful performance insights ([#7101](https://github.com/MetaMask/core/pull/7101))

## [4.0.0]

### Changed
Expand Down
2 changes: 1 addition & 1 deletion packages/core-backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ sequenceDiagram
#### Key Flow Characteristics

1. **Initial Setup**: BackendWebSocketService establishes connection, then AccountActivityService subscribes to selected account. Backend automatically sends a system notification with all chains that are currently up. AccountActivityService tracks these chains internally and notifies TokenBalancesController, which increases polling interval to 5 min
2. **Chain Status Tracking**: AccountActivityService maintains an internal set of chains that are 'up' based on system notifications. On disconnect/error, it marks all tracked chains as 'down' before clearing the set
2. **Chain Status Tracking**: AccountActivityService maintains an internal set of chains that are 'up' based on system notifications. On disconnect, it marks all tracked chains as 'down' before clearing the set
3. **System Notifications**: Backend automatically sends chain status updates (up/down) upon subscription and when status changes. AccountActivityService forwards these to TokenBalancesController, which adjusts polling intervals (up: 5min, down: 30s + immediate fetch)
4. **User Account Changes**: When users switch accounts, AccountActivityService unsubscribes from old account and subscribes to new account. Backend sends fresh system notification with current chain status for the new account
5. **Connection Resilience**: On reconnection, AccountActivityService resubscribes to selected account and receives fresh chain status via system notification. Automatic reconnection with exponential backoff
Expand Down
10 changes: 5 additions & 5 deletions packages/core-backend/src/AccountActivityService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -686,11 +686,11 @@ describe('AccountActivityService', () => {
timestamp: 1760344704595,
});

// Publish WebSocket ERROR state event - should flush tracked chains as down
// Publish WebSocket DISCONNECTED state event - should flush tracked chains as down
rootMessenger.publish(
'BackendWebSocketService:connectionStateChanged',
{
state: WebSocketState.ERROR,
state: WebSocketState.DISCONNECTED,
url: 'ws://test',
reconnectAttempts: 2,
timeout: 10000,
Expand All @@ -701,7 +701,7 @@ describe('AccountActivityService', () => {
);
await completeAsyncOperations(100);

// Verify that the ERROR state triggered the status change for tracked chains
// Verify that the DISCONNECTED state triggered the status change for tracked chains
expect(statusChangedEventListener).toHaveBeenCalledWith({
chainIds: ['eip155:1', 'eip155:137', 'eip155:56'],
status: 'down',
Expand All @@ -720,11 +720,11 @@ describe('AccountActivityService', () => {

mocks.getSelectedAccount.mockReturnValue(null);

// Publish WebSocket ERROR state event without any tracked chains
// Publish WebSocket DISCONNECTED state event without any tracked chains
rootMessenger.publish(
'BackendWebSocketService:connectionStateChanged',
{
state: WebSocketState.ERROR,
state: WebSocketState.DISCONNECTED,
url: 'ws://test',
reconnectAttempts: 2,
timeout: 10000,
Expand Down
27 changes: 15 additions & 12 deletions packages/core-backend/src/AccountActivityService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,15 @@ export class AccountActivityService {
status: data.status,
timestamp,
});

log(
`WebSocket status change - Published tracked chains as ${data.status}`,
{
count: data.chainIds.length,
chains: data.chainIds,
status: data.status,
},
);
}

/**
Expand All @@ -467,11 +476,8 @@ export class AccountActivityService {
// WebSocket connected - resubscribe to selected account
// The system notification will automatically provide the list of chains that are up
await this.#subscribeToSelectedAccount();
} else if (
state === WebSocketState.DISCONNECTED ||
state === WebSocketState.ERROR
) {
// On disconnect/error, flush all tracked chains as down
} else if (state === WebSocketState.DISCONNECTED) {
// On disconnect, flush all tracked chains as down
const chainsToMarkDown = Array.from(this.#chainsUp);

if (chainsToMarkDown.length > 0) {
Expand All @@ -481,13 +487,10 @@ export class AccountActivityService {
timestamp: Date.now(),
});

log(
'WebSocket error/disconnection - Published tracked chains as down',
{
count: chainsToMarkDown.length,
chains: chainsToMarkDown,
},
);
log('WebSocket disconnection - Published tracked chains as down', {
count: chainsToMarkDown.length,
chains: chainsToMarkDown,
});

// Clear the tracking set since all chains are now down
this.#chainsUp.clear();
Expand Down
Loading
Loading