Startup with language packages is about six times slower since v0.41.49; the v0.41.51 release run failed on it #581
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
h-dv/code-index#581
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What happened
The release run for the tag
v0.41.51(run 2591) failed in the job "Windows archive smoke (msvc)", so no release was created for that tag. Intests/release_language_smoke.ps1the firstcode-index query search_symbolsafter a completedcode-index indexwas refused:The script treats a non-zero exit as fatal. The daemon the call had started kept running, and the CI step did not end until the job's 30-minute timeout.
Cause: a regression that shipped in v0.41.49
The same step on earlier release runs (Windows runner, time from the
languages listoutput to the script's last line):languages enableindex+languages listqueryThe one-shot wait budget is 10 s, so v0.41.49 and v0.41.50 passed by a margin of 0.2–0.6 s.
Reproduced on Linux (12 cores, the released
linux-x86_64archives, the seven-language fixturetests/release-smoke/languages, all seven first-party packages enabled, fresh store, no daemon):languages enableindexquery, first / second call81ff0158)a7ce9603)3d0a584c)5d31f4d9)v0.41.51 did not make it slower; it ran on a slightly slower day.
The second query is as slow as the first: with no daemon running, every one-shot call on a project with seven enabled packages costs 8–9 s, although the index is complete and nothing is reparsed (
parse_count: 0,stat_skipped: 7).Debug timeline of one such call on v0.41.49 (
CODE_INDEX_LOG=debug): "single-process startup: language enablement" 2.4 s, "bundled adoption" 1.4 s, "package host" 1.5 s, then 1.6 s, 0.9 s, 0.5 s, 0.35 s and 0.36 s between log lines that are adjacent on v0.41.48. v0.41.48 has one gap of 1.5 s (package discovery) and no other. The code isreconcile_local_snapshotincrates/mcp-server/src/main.rs, which since v0.41.49 callspackages::index_hosttwice and runs enablement, activation reconciliation, automatic enablement and bundled adoption around it. Which function costs what is not attributed yet.Why no gate saw it
Wanted
retryablefor a bounded time and says so, and stops a daemon it started so the step can end.State of the v0.41.51 tag
The tag exists on
3c8742bfand has no release. The fixes it contains (#573, #575, #576, #578) are on master and ship with the next release.The cost grows with the package store (measured 2026-11-12)
The figures in the report were taken with only the seven first-party packages installed. With a store as a developer machine really has it (45 installed packages, 141 MiB, most of them not used by the project) the same steps cost:
3c8742bf(= v0.41.49 … v0.41.51 behaviour)languages enable(7 languages)indexquery search_symbolsindex_incompleteSame machine, shipped build shape, fresh project and no daemon; the store was seeded with a copy of the machine's installed packages.
Cause (attributed by counting, not by timing). Since
f73682d5the answer "which language names are available" (languages::known_language_names_in→Store::installed) reads every archive in the user's store and computes two content hashes for each. One process asks about 22 times, mostly throughConfig::warnings(). With seven packages one round costs about 0.33 s; the count per process was 357 archive parses forlanguages enable, 189 forindex, 196 for a one-shot query. v0.41.48 shows the same figures with 7 and with 45 installed packages, so its cost does not grow with the store.State of the fix. A first candidate reuses the parse result per process (keyed on the complete archive bytes; signature, trust, approval and enablement checks unchanged). It restores the old figures for the seven-package case, but still reads every installed archive on each of the 22 rounds and hashes each one once, so it remains 3.4 times slower than v0.41.48 with the large store. It went back for a second round with this requirement: the cost of startup,
indexandlanguages enablemust not grow with packages that are installed but not used, and the gate counts that.Why no gate saw it. Every CI runner and every test fixture has an empty or seven-package store, and the workspace test build compiles the parsers in. The gate for the fix therefore counts archive reads and hashes with extra unused packages installed, instead of bounding a time.
Released in v0.41.52 (
7fc7fb20, release run 8656, 15 assets). Verified on the installed Linux x86_64 binaries (code-index 0.41.52 (7fc7fb20)):Steps of the release smoke, fresh project, no daemon running, seconds. The machine was loaded by other builds during the measurement (load 8 to 19), both releases alike.
index_incompleteThe cost no longer depends on how many packages are installed.
Windows archive smoke of the release run (the step that stopped v0.41.51), compared with the v0.41.50 release run 8553: enable 20.0 s (was about 26.7 s), index 6.6 s and list 0.9 s (were about 11 s together), query 3.9 s without a retry (was about 9.8 s against a budget of 10 s). The script now prints each step's time, so the next release run can be compared directly.
Not covered: nothing measured on Windows with a large package store; the Windows figures are the 7-package smoke only.