Repository navigation
Speed up bun and yarn-berry rescans (#1606, #1607) - #1693
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A rescan of an already-patched project got about 20% slower on bun and yarn berry since early October. Most of it was work every scan repeats per pair: checking each patched package against every lock entry from other locks, rebuilding a canonical purl string for every crawled package several times over, keying every purl to look for PyPI spellings in projects with no PyPI pins, and re-reading every berry lock entry once per root `resolutions` selector. - The cross-lock contest looks up only the lock entries of the ref's own package, and each tree's wiring by tree. - canonical_base_purl returns an already canonical purl as is. - The PyPI-equivalence passes skip non-PyPI purls. - A berry lock builds its selector index once, on first use. Scan output is unchanged; the fast path is tested against the full rebuild. Refs #1606, #1607 Assisted-by: Claude Code:claude-opus-5-5
- The contest skips PyPI spelling checks for non-PyPI refs, and the index keys live beside ResolvedElsewhere::covers so the two can't drift apart. - The berry selector index maps names to descriptors and looks them up without allocating. - canonical_base_purl's fast path and rebuild share one list of the types that fold spelling. - adopt_pypi_equivalents treats a `pkg:PyPI/` purl as PyPI, matching the query side. Refs #1606, #1607 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit ddf8f9d. Configure here.
|
Labeled Ready for review (burn-down agent).
Generated by Claude Code |
|
Labeled Ready for review (burn-down agent).
Generated by Claude Code |
Final review briefWhat it does. A performance-only change to the shared scan path, aimed at the bun and yarn-berry rescan regression (#1606, #1607). It indexes cross-lock contest candidates by purl and versionless name instead of a full ref × entry × wiring sweep, skips the Risk: low. No output change intended (scan
Look here
Verified
Changes I made: none. Auto-merge is armed, so approving sends this straight to the merge queue. Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Refs #1606
Refs #1607
This PR removes most of the bun and yarn berry rescan regression, but not all of it. Both issues stay open for the remaining part (see Not fixed here), so they're linked with
Refs, notFixes.Root cause
Callgrind of
bun-isolated/rescanandyarn-berry/rescan, comparing main (aa69125) with the bench base (cc938aa6) on the same fixture, showed the same new cost in both scans. It sits in the scan path every ecosystem shares, and it comes from repeated per-pair purl work:Discovery::contest_across_locks(every ref × every lock entry from elsewhere × every wiring, each pair re-splitting purls)PurlKey::new/canonical_base_purl(about 31k calls: every crawled purl keyed in 5+ separate passes, each rebuilt withformat!)scan::with_pypi_equivalents+adopt_pypi_equivalents(key every purl even when the project has no PyPI pin)berry_selector_routes_nothing(re-parses every live entry's locator for each rootresolutionsselector)Total instructions: bun-isolated/rescan 525M (base) → 715M (main) → 630M. yarn-berry/rescan 1,299M → 1,340M → 1,168M.
Changes
contest_across_locksindexes theelsewhereentries by exact purl and by versionless name. Every matchResolvedElsewhere::coverscan make uses one of those two spellings, so each ref now checks only its own package's entries. Wiring is grouped by install tree. Candidates are visited in their original order, so the contesterfindpicks is the same entry as before.canonical_base_purlreturns a purl as is when it is already canonical: trimmed, no?/#/%, a lowercase type that isn'tpypi/nuget/composer. That covers nearly every crawled npm/cargo/gem purl. A new test checks the fast path against the full rebuild on edge-case inputs.with_pypi_equivalentsskips non-PyPI purls and builds its key set only when an equivalent spelling actually exists.adopt_pypi_equivalentsreturns early when no lockfile-only PyPI pin exists.BerryLockbuilds its selector index (resolved urls, names,(name, descriptor)pairs) once, on first use, through aOnceLock.berry_selector_routes_nothinganswers from that index. Entries are never changed afterberry_entriesbuilds them, so the cache can't go stale.Evidence
Scan
--jsonoutput on the bun rescan fixture is byte-identical before and after (durationMsaside).socket-patch-bench compare, mainaa69125(base) vs this PR (head), 20 interleaved pairs, 4 vCPU sandbox:yarn-berry/rescanyarn-berry/hostedbun-isolated/rescanbun-isolated/hostedbun/hostedAgainst the issues' original base
cc938aa6(25 pairs):yarn-berry/rescan+10.1% [+6.8, +14.1] (was +21.4%) andbun-isolated/rescan+14.4% [+10.0, +19.4] (was +21.3%).New tests:
utils::purl_key::tests::canonical_fast_path_matches_the_rebuildvendor::lock_inventory::yarn::selector_tests::one_lock_answers_every_selector_from_its_indexcommands::scan::tests::pypi_equivalents_add_spellings_only_for_lockfile_only_pypi_pinsThese are behavior-pinning tests: the change is a performance change, so there is no red→green result. The bench numbers above are the regression evidence.
Local checks on
ddf8f9d(sandbox runs as root, uid 0):cargo fmt --all -- --check: cleancargo clippy --workspace --all-features -- -D warnings: cleancargo test --workspace --all-features --no-fail-fast: 14,834 passed, 10 failed. All 10 are environmental:0o555directory fails, which can't happen as root:copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_*,pypi_requirements::wire_failure_*,in_process_redirectx3,repairx2.e2e_bun_lockb::binary_shared_bundled_record_hosted_pin_is_managed) downloads a blob from the realpatches-api.socket.dev, which the sandbox proxy refuses./code-review highreported 8 findings. I fixed 7:adopt_pypi_equivalentscase, a shared folding-types list, index keys moved next tocovers, PyPI-only spelling scan, an allocation-free berry index, aHashMapindex, and theis_pypi_purlscheme case. I skipped 1: returningCowfromcanonical_base_purlwould save one copy per purl, but it widens a public API, and the fast path already removes the rebuild cost.Not fixed here (why the issues stay open)
.bunstore liveness walk (live_bun_store_entries_sync/read_bun_store_node_modules_sync, about 95M instructions) is the With Bun's isolated linker,vexrefuses every hosted patch as not_applied after the usual in-placebun install, because it checks orphanednode_modules/.bunregistry entries that Bun never removes (regression from #496) #599 orphan-entry check, already tuned in Perf regression: bun/hosted wall +110% (1169ae68, #472) #578. Making it cheaper means reusing the crawl's own listings, which is a separate change to the crawler.referencesrequest (127 → 128). The withdrawn-pin sweep (Restore withdrawn hosted pins upstream during scan #1592) and the hosted redirect each fetch references for the same pinned uuids. Sharing that answer needs a per-run reference cache inApiClient(or the sweep passing its map to the redirect). That changes the client for every caller, so I left it for a follow-up.cc938aa6, and wall is still above CPU growth. Not profiled further.https://claude.ai/code/session_01RPAAUNEcDh9q9bfJSTLHFs
Note
Low Risk
Performance refactors on hot scan paths with behavior-pinning tests and byte-identical scan JSON; discovery contest semantics are intended to be unchanged.
Overview
Speeds up shared scan/discovery work that regressed bun-isolated and yarn-berry rescans by cutting repeated purl canonicalization and full lock-entry scans, without changing scan JSON output (aside from timing).
VEX
contest_across_locksnow indexeselsewherelock entries by exact purl and versionless name, groups wiring purls per install tree, and only considers candidate entries for each ref’s package—replacing nested scans over every ref × entry × wiring pair.canonical_base_purladds a fast path that returns already-canonical npm/cargo-style purls unchanged instead of always rebuilding viaformat!.Scan PyPI equivalent handling (
with_pypi_equivalents,adopt_pypi_equivalents) skips non-PyPI purls, lazily builds dedup keys only when equivalents exist, and bails early when there are no lockfile-only PyPI pins;is_pypi_purlcentralizes case-insensitive PyPI type detection.Yarn Berry caches a per-lock
SelectorIndex(OnceLock) soberry_selector_routes_nothingno longer re-walks live entries for every rootresolutionsselector.New unit tests lock equivalence of the fast paths vs the old behavior.
Reviewed by Cursor Bugbot for commit ddf8f9d. Configure here.
Generated by Claude Code