vsx-registry: startup floods OVSX registries with per-builtin queries when updateInstalled() races the frontend plugin sync
#17,769 opened on Jul 8, 2026
Repository metrics
- Stars
- (18,676 stars)
- PR merge metrics
- (Avg merge 15d 4h) (75 merged PRs in 30d)
Description
Bug Description:
VSXExtensionsModel can flood the configured OVSX registries with one metadata query per deployed builtin at startup, without the user ever opening the Extensions view.
Mechanism:
- The model is instantiated eagerly (
VSXExtensionsContributionis aFrontendApplicationContribution), andupdateInstalled()callsrefresh(id, version)for every id returned bygetInstalledPluginIds()— which includes all deployed builtins. refresh()is supposed to skip locally-known plugins viashouldRefresh()(extension.plugin === undefined), but that guard readsHostedPluginSupport.getPlugin(), whosecontributionsmap is only populated bysyncPlugins().updateInstalled()re-runs immediately on everyHostedPluginWatcher.onDidDeployevent, while the re-sync that populates the map is debounced (50 ms) and needs RPC round trips. When the frontend connects while the backend is still deploying (cold start — e.g. one backend per session in a container), the guard loses the race and every builtin looks unknown.- Each lost race costs
findLatestCompatibleExtensionper builtin = 2 queries (targetPlatform+universal,OVSXApiFilterImpl) × N registries (OVSXRouterClientfans out to all entries inuse). With ~60 builtins and 2 registries that is ~240 HTTP calls per page load.
Suggested fix: make the guard race-free. updateInstalled() already fetches the deployed versioned ids into this.deployed before the refresh loop, so skipping refresh(id, version) when `${id}@${version}` is in that set avoids the dependency on frontend sync timing. We applied exactly that as a downstream override and it eliminates the storm with no UI regression (installed/builtin entries render from local deployment metadata, as intended).
Steps to Reproduce:
- Configure
--ovsx-router-configwith 2 registries inuse, and deploy the builtins bundle via--plugins=local-dir:...(~60 plugins). - Start the backend and connect a browser page while plugin deployment is still in progress (reliably reproduces when the backend cold-starts per session).
- Watch outgoing traffic: ~4
/api/v2/-/query?extensionId=vscode.*&extensionVersion=...&targetPlatform=...calls per builtin (2 target platforms × 2 registries), with the Extensions view never opened.
Additional Information
- Operating System: any (observed on Linux containers and macOS)
- Theia Version: 1.72.3 and 1.73 (relevant code identical in both)