Skip to content

fix(notification-services-controller): skip no-op state writes - #10275

Open
juanmigdr wants to merge 1 commit into
mainfrom
chore/skip-no-op-notification-state-writes
Open

juanmigdr wants to merge 1 commit into
mainfrom
chore/skip-no-op-notification-state-writes

Conversation

@juanmigdr

@juanmigdr juanmigdr commented Sep 16, 2026

Copy link
Copy Markdown
Member

Explanation

listAccounts, fetchAndUpdateMetamaskNotifications, and markMetamaskNotificationsAsRead each rebuild an array (a spread of a Set, or a .map() over the notification list) and assign it back to state unconditionally, even when the result is identical to what's already there.

this.update() is backed by Immer, and Immer only compares by reference. A new array with the exact same contents still counts as "changed" to Immer, so every one of these calls published a stateChange and triggered a full state persist, whether or not anything actually changed.

listAccounts is the one that matters most in practice: it's called from several unrelated public methods just to read the current accounts array (not because the caller cares about diffing accounts), so on a wallet with a lot of accounts under one SRP it was rewriting subscriptionAccountsSeen far more often than the account set itself ever changes.

Before: any call to these three methods writes and persists, every time.
After: they only write when the account set / notification list / read list actually differs from what's stored.

Confirmed on a real device with a large multi-account wallet — before this fix, most listAccounts calls during a normal session were reassigning an identical array; after, they don't touch state at all unless an account was actually added or removed.

References

None

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Low Risk
Behavioral change is limited to suppressing redundant state persistence and events; observable state when data actually changes should match prior behavior.

Overview
Stops unnecessary controller state updates when account listing, notification fetch, or mark-as-read rebuild arrays that are deep-equal to what is already stored. Immer treated every new array reference as a change, so stateChange and persisted state fired even when nothing meaningful changed—especially on repeated listAccounts calls in multi-account wallets.

subscriptionAccountsSeen is written only when the keyring account set actually adds or removes addresses (not on every listAccounts invocation). metamaskNotificationsList is updated after fetch or mark-as-read only when lodash isEqual says the new list differs. metamaskNotificationsReadList grows only when at least one new read ID is introduced.

Tests assert no stateChange (or no Immer patch on the notifications list) for idempotent repeats, and that updates still occur when data genuinely changes.

Reviewed by Cursor Bugbot for commit 8febbf2. Bugbot is set up for automated code reviews on this repo. Configure here.

@juanmigdr
juanmigdr requested review from a team as code owners September 16, 2026 17:34
@juanmigdr juanmigdr added the area-performance Issues relating to slowness of app, cpu usage, and/or blank screens. label Sep 16, 2026
@juanmigdr
juanmigdr deployed to default-branch September 16, 2026 17:34 — with GitHub Actions Active
listAccounts, fetchAndUpdateMetamaskNotifications, and
markMetamaskNotificationsAsRead all rebuild an array (spread/map) and
reassign it unconditionally, even when the result is identical to
what's already in state. Since state.update() uses Immer, assigning a
new-but-equal reference still emits a patch and publishes stateChange,
which triggers a full state persist.

listAccounts in particular gets called by several unrelated methods
just to read the current accounts array, so on a wallet with many
accounts this fired a lot more often than the account set itself
actually changed.

Add a cheap check before each of the three writes so they're skipped
when nothing changed, and add tests asserting no stateChange is
published in that case (and that it still is when something genuinely
changes).
@juanmigdr
juanmigdr force-pushed the chore/skip-no-op-notification-state-writes branch from 70479a2 to 8febbf2 Compare September 16, 2026 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-performance Issues relating to slowness of app, cpu usage, and/or blank screens.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant