Skip to content

fix: never serve Accounts API balances from cache - #9867

Closed
salimtb wants to merge 1 commit into
mainfrom
fix/accounts-api-force-update-cache-bypass
Closed

fix: never serve Accounts API balances from cache#9867
salimtb wants to merge 1 commit into
mainfrom
fix/accounts-api-force-update-cache-bypass

Conversation

@salimtb

@salimtbsalimtb commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

AccountsApiDataSource now invalidates cached balance queries before every fetch and fetches v5/v6 balances with staleTime: 0, gcTime: 0, so balances always hit the network in every flow (polling, forced refresh, websocket-triggered) and are never retained for reuse. Previously non-forced polls could reuse balances cached for up to 60s and forceUpdate: true only shrank the cache window to 100ms, so pull-to-refresh and post-transaction refreshes could show stale balances.

Extension PR: MetaMask/metamask-extension#45494
Mobile PR: MetaMask/metamask-mobile#34748

Explanation

References

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

AccountsApiDataSource now invalidates cached balance queries before
every fetch and fetches v5/v6 balances with staleTime: 0, gcTime: 0,
so balances always hit the network in every flow (polling, forced
refresh, websocket-triggered) and are never retained for reuse.
Previously non-forced polls could reuse balances cached for up to 60s
and forceUpdate: true only shrank the cache window to 100ms, so
pull-to-refresh and post-transaction refreshes could show stale
balances.
// balance queries first (fetchQuery refetches invalidated queries
// regardless of freshness), then fetch with staleTime/gcTime 0 so the
// response is neither read back nor retained.
const fetchOptions = { staleTime: 0, gcTime: 0 };

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

passing this to 0 now , we can also consider removing it

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.

Reverts this PR:
#9591

Worth double checking - I think instead of caching, we should attempt debouncing. But open to discuss

// regardless of freshness), then fetch with staleTime/gcTime 0 so the
// response is neither read back nor retained.
const fetchOptions = { staleTime: 0, gcTime: 0 };
await this.#apiClient.accounts.invalidateBalances();

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

staleTime/gcTime only apply to this one call, old entries in the shared cache keep their 60s freshness. Invalidating first marks them stale for all consumers and forces an unconditional refetch, guaranteeing balances never come from cache.

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.

Yes, 100% agreed here

@salimtb

Copy link
Copy Markdown
ContributorAuthor

@metamaskbot publish-preview

@github-actions

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.6.1-preview-72d4782
@metamask-previews/accounts-controller@39.1.0-preview-72d4782
@metamask-previews/address-book-controller@7.1.2-preview-72d4782
@metamask-previews/ai-controllers@0.8.0-preview-72d4782
@metamask-previews/analytics-controller@2.0.0-preview-72d4782
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-72d4782
@metamask-previews/announcement-controller@8.1.0-preview-72d4782
@metamask-previews/app-metadata-controller@2.0.1-preview-72d4782
@metamask-previews/approval-controller@9.0.2-preview-72d4782
@metamask-previews/assets-controller@13.1.2-preview-72d4782
@metamask-previews/assets-controllers@111.1.0-preview-72d4782
@metamask-previews/authenticated-user-storage@3.0.1-preview-72d4782
@metamask-previews/base-controller@9.1.0-preview-72d4782
@metamask-previews/base-data-service@0.1.3-preview-72d4782
@metamask-previews/bitcoin-regtest-up@1.0.0-preview-72d4782
@metamask-previews/bridge-controller@79.2.0-preview-72d4782
@metamask-previews/bridge-status-controller@75.1.0-preview-72d4782
@metamask-previews/build-utils@3.0.4-preview-72d4782
@metamask-previews/chain-agnostic-permission@1.7.0-preview-72d4782
@metamask-previews/chomp-api-service@4.0.0-preview-72d4782
@metamask-previews/claims-controller@0.6.0-preview-72d4782
@metamask-previews/client-controller@1.0.1-preview-72d4782
@metamask-previews/client-utils@2.0.2-preview-72d4782
@metamask-previews/compliance-controller@2.1.0-preview-72d4782
@metamask-previews/composable-controller@12.0.1-preview-72d4782
@metamask-previews/config-registry-controller@2.0.1-preview-72d4782
@metamask-previews/connectivity-controller@0.3.0-preview-72d4782
@metamask-previews/controller-utils@12.3.0-preview-72d4782
@metamask-previews/core-backend@8.1.1-preview-72d4782
@metamask-previews/delegation-controller@3.0.2-preview-72d4782
@metamask-previews/earn-controller@12.2.4-preview-72d4782
@metamask-previews/eip-5792-middleware@3.0.5-preview-72d4782
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-72d4782
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-72d4782
@metamask-previews/ens-controller@19.1.6-preview-72d4782
@metamask-previews/eth-block-tracker@15.0.1-preview-72d4782
@metamask-previews/eth-json-rpc-middleware@24.0.0-preview-72d4782
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-72d4782
@metamask-previews/foundryup@1.0.1-preview-72d4782
@metamask-previews/gas-fee-controller@26.3.1-preview-72d4782
@metamask-previews/gator-permissions-controller@5.0.1-preview-72d4782
@metamask-previews/geolocation-controller@1.0.0-preview-72d4782
@metamask-previews/java-tron-up@1.0.0-preview-72d4782
@metamask-previews/json-rpc-engine@10.5.0-preview-72d4782
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-72d4782
@metamask-previews/keyring-controller@27.1.1-preview-72d4782
@metamask-previews/kyc-controller@0.0.0-preview-72d4782
@metamask-previews/local-node-utils@1.0.0-preview-72d4782
@metamask-previews/logging-controller@9.0.0-preview-72d4782
@metamask-previews/message-manager@14.1.2-preview-72d4782
@metamask-previews/messenger@2.0.0-preview-72d4782
@metamask-previews/messenger-cli@0.2.0-preview-72d4782
@metamask-previews/money-account-api-data-service@0.4.0-preview-72d4782
@metamask-previews/money-account-balance-service@2.4.1-preview-72d4782
@metamask-previews/money-account-controller@1.0.0-preview-72d4782
@metamask-previews/money-account-upgrade-controller@3.0.1-preview-72d4782
@metamask-previews/money-account-utils@1.1.0-preview-72d4782
@metamask-previews/multichain-account-service@13.0.1-preview-72d4782
@metamask-previews/multichain-api-middleware@4.0.2-preview-72d4782
@metamask-previews/multichain-network-controller@3.2.3-preview-72d4782
@metamask-previews/multichain-transactions-controller@7.1.2-preview-72d4782
@metamask-previews/name-controller@9.1.2-preview-72d4782
@metamask-previews/network-connection-banner-controller@0.1.2-preview-72d4782
@metamask-previews/network-controller@35.0.1-preview-72d4782
@metamask-previews/network-enablement-controller@6.0.3-preview-72d4782
@metamask-previews/notification-services-controller@26.0.1-preview-72d4782
@metamask-previews/passkey-controller@3.0.0-preview-72d4782
@metamask-previews/permission-controller@13.1.1-preview-72d4782
@metamask-previews/permission-log-controller@5.1.0-preview-72d4782
@metamask-previews/perps-controller@11.0.0-preview-72d4782
@metamask-previews/phishing-controller@17.3.1-preview-72d4782
@metamask-previews/platform-api-docs@0.0.0-preview-72d4782
@metamask-previews/polling-controller@16.0.9-preview-72d4782
@metamask-previews/preferences-controller@23.1.0-preview-72d4782
@metamask-previews/profile-metrics-controller@4.0.3-preview-72d4782
@metamask-previews/profile-sync-controller@29.0.0-preview-72d4782
@metamask-previews/ramps-controller@20.0.0-preview-72d4782
@metamask-previews/rate-limit-controller@7.0.1-preview-72d4782
@metamask-previews/react-data-query@0.2.2-preview-72d4782
@metamask-previews/remote-feature-flag-controller@5.0.0-preview-72d4782
@metamask-previews/sample-controllers@5.0.4-preview-72d4782
@metamask-previews/seedless-onboarding-controller@10.1.1-preview-72d4782
@metamask-previews/selected-network-controller@26.1.6-preview-72d4782
@metamask-previews/sentinel-api-service@1.0.0-preview-72d4782
@metamask-previews/shield-controller@6.0.0-preview-72d4782
@metamask-previews/signature-controller@39.2.9-preview-72d4782
@metamask-previews/smart-transactions-controller@25.1.0-preview-72d4782
@metamask-previews/snap-account-service@2.1.2-preview-72d4782
@metamask-previews/social-controllers@2.7.1-preview-72d4782
@metamask-previews/solana-test-validator-up@1.0.0-preview-72d4782
@metamask-previews/stellar-quickstart-up@0.0.0-preview-72d4782
@metamask-previews/storage-service@1.0.2-preview-72d4782
@metamask-previews/subscription-controller@7.0.0-preview-72d4782
@metamask-previews/transaction-controller@69.5.2-preview-72d4782
@metamask-previews/transaction-pay-controller@26.3.0-preview-72d4782
@metamask-previews/user-operation-controller@41.2.8-preview-72d4782
@metamask-previews/wallet@10.0.0-preview-72d4782
@metamask-previews/wallet-cli@0.0.0-preview-72d4782

salimtb added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 13, 2026
Align with MetaMask/core#9867: invalidate balance queries and fetch
with staleTime/gcTime 0 on every AccountsApiDataSource fetch.
salimtb added a commit to MetaMask/metamask-extension that referenced this pull request Aug 13, 2026
Align with MetaMask/core#9867: invalidate balance queries and fetch
with staleTime/gcTime 0 on every AccountsApiDataSource fetch.
@salimtb

Copy link
Copy Markdown
ContributorAuthor

closing this one to do a revert instead: #9870

@salimtbsalimtb closed this Aug 13, 2026
github-merge-queueBot pushed a commit that referenced this pull request Aug 20, 2026
## Explanation
`AccountsApiDataSource` polls for balances every 30s, but the underlying
`ApiPlatformClient` caches responses for 60s (`STALE_TIMES.BALANCES`).
Since the poll didn't pass `forceUpdate`, every other tick was served
the cached response instead of hitting the network — balances only
actually refreshed every ~60s.
This passes `forceUpdate: true` on the poll's fetch call so it always
bypasses the cache, same as other call sites (unlock, account switch, tx
confirmation) already do.
Kept the fix scoped to just the poll instead of removing caching
everywhere, since #9591 added `forceUpdate` specifically to avoid bursts
of duplicate calls when multiple triggers fire close together, and the
unmerged #9867 ran into that regression by disabling the cache globally.
## References
* Related to #9591
* Related to (closed) #9867
## Checklist
- [x] 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
- [x] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md)
- [ ] I've introduced [breaking
changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md)
in this PR and have prepared draft pull requests for clients and
consumer packages to resolve them
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Narrow cache-bypass on the 30s poll path only; does not change auth or
other fetch sites. Slightly more Accounts API traffic on that interval,
which is the intended behavior.
> > **Overview**
> Fixes `AccountsApiDataSource` polling serving stale balances on
roughly every other tick. The 30s poll used `fetch()` without
`forceUpdate`, so the 60s TanStack balances cache
(`STALE_TIMES.BALANCES`) won.
> > Recurring `pollFn` now passes `forceUpdate: true` so each tick hits
the network (`staleTime: 0, gcTime: 0`), matching other authoritative
refreshes. Bypass is scoped to the poll only so nearby unlock/switch/tx
triggers still share the cache.
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d162463. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@salimtb@Prithpal-Sooriya