fix: invalidate discovery caches on config change and wire Clear Cache to native finder - #26
fix: invalidate discovery caches on config change and wire Clear Cache to native finder#26StellaHuang95 wants to merge 1 commit into
Conversation
|
🔒 Automated review in progress — @StellaHuang95 is auto-reviewing this PR. |
2fb6f20 to
e76c8d7
Compare
e76c8d7 to
bad44fb
Compare
… caches The native finder's soft cache keyed only on scope (all/kind/URI) and returned before rebuilding the effective PET ConfigurationOptions, so workspace, search-path, and tool-path setting changes served stale discovery results. The Clear Cache command also left the finder's in-memory map and PET's on-disk cache untouched (clearCacheDirectory had no callers). - Tag each result-cache entry with the effective ConfigurationOptions and a cache generation; a soft hit is valid only when the saved config still equals the current one (configurationEquals, per-key), so unrelated scopes keep their entries and only the changed key is invalidated. - A hard refresh captures one config, threads it through configure + the result tag, and a clear that lands mid-refresh advances the generation so the in-flight result cannot repopulate the map. - Add NativePythonFinder.clearCache(): empty the in-memory map, send PET's live `clear` RPC (bounded, best-effort), and empty the on-disk cache directory; wire it into the Clear Cache command with clearCacheDirectory as the pre-start/no-server disk fallback. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
bad44fb to
be1cb7d
Compare
|
GitHub cannot anchor PR review comments to unchanged lines in the diff. Falling back to a general PR comment for src/managers/common/nativePythonFinder.ts:L699.
|
| private async refreshViaJsonCli( | ||
| options: NativePythonEnvironmentKind | Uri[] | undefined, | ||
| config: ConfigurationOptions, | ||
| ): Promise<NativeInfo[]> { |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
The CLI fallback uses the captured ConfigurationOptions but rereads venvFolders when the queued task executes. A settings change between scheduling and execution can therefore make its search paths differ from the configuration used for coalescing and cache tagging. Capture these paths with the refresh task as well.
Problem
Two verified defects in native Python discovery caching:
NativePythonFinderImpl's in-memory soft cache keyed only on the refresh scope (all/ kind / URI list) and returned cached results before rebuilding or comparing the effective PETConfigurationOptions. A change to workspace folders, extra search paths, or the conda/pipenv/poetry executable path or cache directory kept serving stale discovery results until an unrelated hard refresh happened.python-envs.clearCachecleared persistent, manager, and shell caches, but neither the finder's in-memoryMapnor PET's on-disk cache directory.clearCacheDirectoryhad zero callers.Root cause
The soft-cache key omitted the effective configuration, and the Clear Cache command had no hook into the native finder or PET's disk cache.
Fix
Cache invalidation (
nativePythonFinder.ts)configurationEqualsis extracted into an exported pure function (property-by-property; order-independent on the two directory arrays) and reused for both PETconfiguregating and soft-cache validation.DiscoveryResultCachestores{ configuration, results, generation }per existing result-cache key. A soft hit is valid only when the key exists, is in the current generation, andconfigurationEquals(entry.configuration, currentConfig)holds — so alternatingall/ kind / URI calls keep their unrelated valid entries and only the changed key is invalidated.handleSoftRefreshbuilds the effective configuration from current settings (reads only, no discovery I/O; build errors propagate) and validates the hit; a miss falls through to a hard refresh.await, builds oneConfigurationOptions, threads it throughconfigure, the CLI fallback, and the result tag, and returns{ results, configuration }.DiscoveryResultCache.setis a no-op when the generation has advanced, so a refresh that began before aclearCache()cannot repopulate the just-cleared map.Clear Cache
NativePythonFinder.clearCache(): Promise<void>clears the in-memory map (advancing the generation), sends PET'sclearJSON-RPC request when a server process is live (bounded timeout, best-effort), and empties the on-disk cache directory viafs.emptyDir. Disk-clear failure propagates when it is the primary mechanism (no live clear) and is only logged when redundant after a successful live clear.extension.tswirespython-envs.clearCacheto the shared finder through a small typedsharedNativeFinderhandle, falling back toclearCacheDirectory(context)when Clear Cache runs before the finder exists. Ordering is preserved (persistent → manager → native → shell), with the native step intry/finallyso shell-profile cleanup still runs.PET's JSON-RPC
clearmethod is present in the consumed binary (it predates theinfomethod this repo already uses); its params are ignored and success repliesnull.Tests
New unit tests under
src/test/managers/common/:nativePythonFinder.discoveryCache.unit.test.ts—configurationEqualsfor every field (order-independence, defined-vs-undefined, fsPath equality) andDiscoveryResultCache(per-key same-config hit; config change invalidating only the affected key; alternatingall/kind/URI not thrashing; hard result tagged with its captured config; explicit clear emptying entries and advancing the generation; a store from a refresh that began before clear not repopulating).nativePythonFinder.clearCache.unit.test.ts— liveclearRPC plus on-disk sweep; no-live-server disk clear; disk sweep still runs after an RPC failure; fail-closed disk-failure propagation as the primary clear and ignored when redundant after a live clear; no-server/no-dir warning; theconfigureclear-race guard (stalelastConfigurationnot resurrected); andclearCacheDirectoryclearing the correct dir before a finder exists (with fsPath comparisons and scoped settings).Pre-commit checks:
npm run lint✓,npm run compile-tests✓,npm run unittest✓ (1743 passing, 5 pending).