You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Avoid a full workspace search per Python file event - #1999
Since 1.42.0 the workspaceHasPython watcher calls findFiles for every created or deleted marker file. Each call starts its own rg --files over the workspace, so a branch switch that touches a thousand Python files starts a thousand searches at once, which is the freeze reported in #1989.
The change stays in workspacePythonContext.ts and follows the approach suggested on the issue:
a created marker file or an opened Python document sets the context to true without a search
deletes and workspace folder changes share one rescan through createSimpleDebounce (500 ms); deletes are skipped while the context is already false
only one search runs at a time, and a rescan requested meanwhile runs once after it
findFiles gets a cancellation token, cancelled when the result is stale or on dispose
setContext is only called when the value changes
The activation scan and the excludes are unchanged.
The workspaceHasPython watcher called findFiles for every created or
deleted marker file, and each call starts its own ripgrep process, so a
branch switch that touches a thousand Python files started a thousand
workspace searches at once.
A created marker file or an opened Python document now sets the context
to true without searching. Deletes and workspace folder changes share
one rescan, debounced by 500 ms. Only one search runs at a time, a
rescan requested meanwhile runs once after it, and a search is
cancelled when its result is stale or the extension is disposed. The
context is only set when its value changes.
The reason will be displayed to describe this comment to others. Learn more.
Checked this branch on Windows 11 against the case in my #1989 comment: the home folder open as the workspace. Your test file passes (35 of 35) and fails on main (18 failures, for example 1,001 scans for 1,000 creates). I also ran a harness with your stubs, a 40 s fake scan and 5 minutes of temp-file events. A delete every 250 ms went from 1,201 findFiles calls, 161 at once, to 1 call. Create then delete every second went from 601 calls, 81 at once, to 301 calls, one at a time. That removes the cause of our commit-limit crash.
One gap: every delete while the key is true still schedules a rescan, so pytest-style churn starts a new rg each cycle. Keeping the Uri that findFiles returned (maxResults 1 gives exactly one) and rescanning only when a delete hits that Uri would make those deletes free.
One case this doesn't cover yet: a single scan that runs for minutes.
Linux, VS Code 1.145.2, Python Environments 1.42.0. The workspace holds a Wine prefix, and Wine's dosdevices/z: is a symlink to /. The scan the extension starts is:
--follow comes from search.followSymlinks (default true), because legacy findFiles passes no followSymlinks option. The scan goes through z: into the whole filesystem.
--no-ignore means .gitignore doesn't stop it either.
I saw up to six of these at once, each using 60–96% of a core for several minutes. I don't know yet why maxResults: 1 didn't end them: run by hand, the same command prints a match within 0.1 s.
With this PR there would be one such rg instead of a pile. But a rescan queued by a delete or a folder change waits for the running search, so those updates would also wait for as long as that scan lasts. Two suggestions:
Bound the search: cancel the token after a timeout (a few seconds), and treat a timeout as "unknown, keep the current value" rather than false.
Use findFiles2 with followSymlinks: false (and useIgnoreFiles on), so the marker check stays inside the workspace whatever the user's search settings are.
Workaround for anyone affected: "search.followSymlinks": false in user settings.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unresolved context-tracking issues can leave Python views enabled after the last marker is deleted or hidden in an existing Python workspace when the initial scan exceeds five seconds.
Hello. Thank you very much for taking the time to work on this, and for your contribution.
Added a couple of commits with some fixes. Will let some other tests run, and get the necessary reviews to get this merged. Thank you!
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The workspace context can remain stale because slow scans are canceled without retry and directory deletions fail to invalidate cached Python-file markers, leaving Python views incorrectly hidden or visible.
No open issues 🎉
2 previously missed issues
In code that hasn't changed since last review
Timed-out Python scans can leave views hidden indefinitely
If a non-Python workspace gains a folder containing Python files and its search takes more than five seconds, this timeout cancels the scan without scheduling a retry. The context stays false and the Python views remain hidden until another qualifying event or reload. Let a still-current scan finish, or provide a recovery path that can eventually complete even when searches consistently exceed this limit.
Deleting a parent directory can leave the Python context stale
If the cached marker is tools.py/main.py, deleting the tools.py directory can deliver only the directory's delete event, without separate events for its children. This exact-key comparison then skips the rescan, leaving the context true after the last Python file is gone. Also invalidate markers beneath a deleted URI, including results from running scans, and add coverage for directory-only delete events.
…kers
The watcher does not report markers removed with a folder whose name does not
match the glob, so the remembered marker could go stale and keep the context
true forever. Deleting any other marker now triggers a debounced stat of the
remembered marker, which clears it and rescans if it is gone.
A created marker that is deleted before it is inspected is now ignored instead
of being logged as an error and triggering a full rescan.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6260a269-c45d-4a86-bb05-4fe45eec917f
Additional Windows evidence and focused verification for this PR / #1989.
Environment: Python Environments 1.42.0, VS Code 1.145.2 (c4453f8ea4), Windows 11 build 26300.9818, 32 GB installed RAM.
A process/memory sampler and Win32_ProcessStartTrace recording captured the following near the end of an affected session (2026-11-12, UTC+08:00):
Time
Live rg.exe
Combined private commit of rg.exe
Total system processes
CPU
Available physical memory
03:27:20
1,806
4.23 GiB
3,944
99.94%
8.74 GiB
03:29:17
2,049
4.97 GiB
4,442
99.94%
8.17 GiB
03:30:38
2,419
6.32 GiB
5,172
99.96%
6.42 GiB
At the final sample, all 2,419 live ripgrep processes had the same VS Code extension-host parent. The process-start records also retain that parent's creation time and ancestry. There were another 2,435 conhost.exe processes. The captured command was the marker search from workspacePythonContext.ts:
search.ripgrep.maxThreads: 2 and search.followSymlinks: false were already applied before this capture. They did not prevent the accumulation: limiting threads within each search does not bound the number of searches. This supports the request-coalescing and single-in-flight changes in this PR.
I checked out PR head 2e994a8 and ran these locally on Windows:
npm ci --ignore-scripts --no-audit --no-fund
npm run compile-tests — passed
ESLint on workspacePythonContext.ts and its unit test file — passed
The focused workspacePythonContext.unit.test.js suite, using the repository's test bootstrap — 59 passing
I did not rerun the full suite or intentionally replay a large filesystem event burst inside a live VS Code window. These observations establish the search-process accumulation and resource pressure; they do not independently establish the cause of earlier Windows kernel bugchecks on this machine. Paths/usernames and unrelated process command lines are omitted.
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
bugIssue identified by VS Code Team member as probable bug
8 participants
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.
Summary
Since 1.42.0 the
workspaceHasPythonwatcher callsfindFilesfor every created or deleted marker file. Each call starts its ownrg --filesover the workspace, so a branch switch that touches a thousand Python files starts a thousand searches at once, which is the freeze reported in #1989.The change stays in
workspacePythonContext.tsand follows the approach suggested on the issue:truewithout a searchcreateSimpleDebounce(500 ms); deletes are skipped while the context is alreadyfalsefindFilesgets a cancellation token, cancelled when the result is stale or on disposesetContextis only called when the value changesThe activation scan and the excludes are unchanged.
Fixes #1989
Testing
A small harness fired watcher events at
registerWorkspacePythonContextand countedfindFilescalls (total / peak concurrent):The final context value was the same before and after in every case.
npm run compile-tests,npm run lint, and a prettier check on the changed filesnpm run unittest: 3521 passing, 12 pending