MetaMask / MetaMask/metamask-extension
[P2] Balance selector recomputes 4x with flat inputs (2026-07-13, 14-hour window)
- Dominant language
- TypeScript
- Stars
- 13.2k
- Forks
- 5.6k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 451
Description
**File:** `ui/selectors/assets.balance-utils.ts`, `@metamask/assets-controller` (`getAggregatedBalanceForAccount`)
**Size:** M
> **The observability window is closing.** This was only visible because of a telemetry defect (#44447), and the span that revealed it is being removed in #44449. Once that ships, this behaviour will no longer be observable in Sentry. The instrumentation that would properly explain it (`AssetsUpdatePipeline`, "enrichment pipeline timing for pushed data source updates") is disabled by #43213. Investigation should begin before the fix drains into the release train.
---
## Problem
On 2026-07-13 between 04:00Z and 17:00Z, the unified balance aggregation (`getAggregatedBalanceForAccount`, reached from `ui/selectors/assets.ts` via `ui/selectors/assets.balance-utils.ts`) recomputed approximately **4.3x more often than baseline**, for exactly 14 hours, with a sharp start and a sharp end (03Z, 04Z, then peak at 16Z, then back to baseline by 18Z).
Every input to that selector was flat across the same window.
| input | baseline to spike |
|---|---|
| Balance selector recomputations | **4.3x** |
| `http.client`, and every individual endpoint (infura, accounts, token, geolocation) | 1.1 to 1.3x |
| WebSocket messages (`BackendWebSocketService Notification`) | 1.3x |
| WebSocket connections | 1.2x |
| `AccountActivityService Transaction Message` | 1.4x |
| User activity (`UI Startup`, `pageload`, `navigation`) | 1.1x |
Request volume, WebSocket push volume, and user activity were unchanged. Only the recomputation count moved.
A Redux selector recomputes when its inputs change by reference. A 4.3x increase in recomputation with no increase in the data feeding it indicates that something began invalidating memoization without new data arriving. Candidate causes:
- A backend response-shape change (reordered arrays, an added field, float noise in prices) that produces new object references on every poll, where the same poll previously left state unchanged.
- A feature flag or remote config change altering dispatch or render cadence.
The sharp 14-hour on/off edge is consistent with a server-side toggle or a deploy, not with a client release (no release shipped in that window, and the effect appeared across 13.38.1, 13.38.2, and 13.39.1 simultaneously).
This is a balance-state concern independent of the telemetry. Recomputing the full balance aggregation per account group, 4x more often than the data warrants, is wasted work on the UI thread for every user, and it indicates state is being marked dirty when the underlying balances have not changed.
**Root cause is not verified.** Every observable input has been ruled out, but payload content and flag state are not visible from Sentry.
---
## Solution
1. Identify what changed at approximately 2026-07-13 04:00Z: backend deploy, feature flag rollout, or remote config change affecting the assets data sources.
2. Verify whether the selector's inputs are stable by reference across polls in which the underlying balance data is unchanged. If a data source produces a fresh object on every poll regardless of content, memoization is defeated.
3. Fix the memoization boundary so unchanged data does not produce new references, or confirm the recomputation is expected and bound it.
---
## Acceptance Criteria
- [ ] The cause of the 2026-07-13 recomputation increase is identified.
- [ ] Balance selector inputs are verified stable by reference when the underlying balances are unchanged.
- [ ] Excess recomputation is eliminated, or documented as expected with a bound on its frequency.
---
## Labels
`team-assets`, `area-performance`
---
## Dependencies
**Evidence:** #44447 (the telemetry defect that surfaced this, with the full Sentry query set)
**Blocked by, for observability:** #44449 removes the span this was measured with
**Related:** #43213 (disabled `AssetsUpdatePipeline`, the instrumentation that would explain the churn)
Contributor guide
Research direction
Start in ui/selectors/assets.balance-utils.ts at getAggregatedBalanceForAccount, reached from ui/selectors/assets.ts, and trace each selector input across polls with unchanged balances. Review the evidence and Sentry query set in #44447, plus the observability changes in #44449 and #43213, while checking backend, feature-flag, and remote-config changes around 2026-07-13 04:00Z. Done means the cause is identified, reference stability is verified, and excess recomputation is eliminated or documented with a frequency bound.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100