perf: cut the popup's serialized storage round trips on cold start - #1583
Open
charllesgalves wants to merge 1 commit into
Open
perf: cut the popup's serialized storage round trips on cold start#1583charllesgalves wants to merge 1 commit into
charllesgalves wants to merge 1 commit into
Conversation
Opening the popup ran roughly fifty chrome.storage round trips one after another before Vue could mount, which is what makes it feel slow to open even with a couple of entries and no encryption configured. Almost all of them come from two places: - BrowserStorage.getStorageLocation() runs at the top of every storage operation, and each call re-read all user settings (one or two round trips, depending on whether settings live in sync storage) plus the managed policy. Nothing was cached, so the same values were fetched over and over. - The popup awaited each async Vuex module in turn, and the advisor store awaited its five insight validations in turn -- with each validation re-reading settings from scratch. Cache the settings and the managed policy, invalidating on chrome.storage.onChanged so other extension contexts are still observed, and coalesce concurrent readers onto a single in-flight read. Then build the store modules and the i18n catalog concurrently, and run the advisor validations concurrently. Managed storage keeps its original contract: no caller waits more than ~10ms on it, and a policy that arrives after that still lands in the cache for later lookups. Measured against a chrome.storage mock that counts calls: 15x UserSettings.updateItems(): 30 gets -> 2 8x ManagedStorage.get(): 8 gets -> 1 Also adds a missing await in the backup store, which read UserSettings.items before the read it depends on had resolved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
perf: cut the popup's serialized storage round trips on cold start
Problem
Opening the popup runs roughly fifty
chrome.storageround trips, one afteranother, before Vue can mount. This is the bulk of the perceived "slow to
open" time, and it happens even with a couple of entries and no encryption
configured — so it isn't decryption or data volume.
Two sources account for nearly all of them:
1.
BrowserStorage.getStorageLocation()runs at the top of every storageoperation, and each call did:
Nothing was cached, so the same values were re-fetched over and over. When
settings live in sync storage,
updateItems()costs two reads (local + sync)rather than one.
2. Everything was awaited in sequence.
popup.tsawaited each async Vuexmodule in turn, and
Advisorawaited its five insight validations in turn —with each validation calling
UserSettings.updateItems()again from scratch,and two of them additionally calling
hasEncryptionKey().Approximate breakdown of the round trips before mount:
UserSettings.updateItems()Accounts.getModule()Advisor.getModule()Backup.getModule()Menu.getModule()(8xManagedStorage.get)Changes
chrome.storage.onChanged, sowrites from other extension contexts (background, options page) are still
observed.
commitItems()invalidates explicitly so it never serves a staleread back to its own caller.
key at a time — the menu store alone reads eight. It is now fetched once.
above and in
getStorageLocation(), so parallel callers don't each re-runthe auto-detection branch (which can also commit settings).
popup.ts.awaitinBackup.getModule(), which readUserSettings.itemsbefore the read it depends on had resolved.ManagedStoragekeeps its original contract: no caller waits more than ~10mson managed storage, and a policy that arrives after the timeout still lands in
the cache for later lookups.
Verification
tsc --noEmitclean;npm run chromebuilds successfully.chrome.storagemock that countsgetcalls:UserSettings.updateItems()ManagedStorage.get()Cache invalidation and concurrent reads were checked in the same harness: a
write followed by a read returns the fresh value, and four concurrent readers
all resolve correctly.
the
--load-extensioncommand-line switch the runner depends on, sotest-runner.jsfails withERR_BLOCKED_BY_CLIENTondevas well as onthis branch — the failure is pre-existing and unrelated to these changes.
🤖 Generated with Claude Code