GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fix yarn classic hosted scan lock rescans (#1423) by mikolalysenko · Pull Request #1426 · SocketDev/socket-patch

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 12, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1423

Summary

A hosted scan of a yarn classic project got about 2.6x slower after #1328. Two
checks that #1328 added re-read the whole yarn.lock once for every patched
package. Each lock is now read once per scan. On the 3000-package / 60-patch
bench, yarn-classic/hosted and yarn-classic/rescan are both about 56%
faster than current main. Rescan is back to its pre-#1328 time.

Root cause

Both are O(patched packages × lock size):

  1. Choosing which served tarballs to fetch
    (hosted::engine::yarn_classic_artifact_targets). For each candidate it ran
    classic_locks_registry_copy, which re-ran scan_blocks over the whole lock
    and split every key. It also ran two whole-lock substring searches per
    candidate: one for the pinned URL and one for the name.
  2. The Yarn classic: a patch that adds a dependency to the package's own package.json leaves vendored yarn.lock with a dangling dependency (offline frozen install fails, lock churns), and hosted silently installs without it #591 served-dependency check in the yarn classic rewriter
    (classic_deps::unlocked_descriptors). For each patched package it split
    every key of the lock into a fresh BTreeSet, even when the package has no
    dependencies.

Changes

  • select_classic_artifacts collects the lock's registry copies
    (classic_registry_copies) once, and only if some candidate still needs
    them. It also collects every URL the lock already pins with a # fragment
    (quoted_fragment_urls) in one lazy pass. The cheap "already pinned" test
    runs first, so a rescan whose grants all carry a sha1 never reads the blocks.
  • The rewriter already splits each key once (heads). The Yarn classic: a patch that adds a dependency to the package's own package.json leaves vendored yarn.lock with a dangling dependency (offline frozen install fails, lock churns), and hosted silently installs without it #591 check now
    looks descriptors up in a HashSet<&str> built from those patterns the first
    time a package needs it (classic_deps::unlocked_descriptors_in). The
    vendored path keeps unlocked_descriptors, because it calls it once per
    package.
  • Selection is unchanged: the same tarballs are fetched and the same
    descriptors are reported as unlocked.

The history has one reverted experiment: a commit that fetched the tarballs
concurrently, then its revert. Code review found that it could hold up to 32
whole tarballs in memory, and that retries would hit the server together.
The wheel fetch window was designed specifically to avoid both. It also gained
almost nothing on the bench, so it was backed out.

Bench (socket-patch-bench compare --runs 20, same 4 vCPU sandbox, bench fixture from #1422)

Against main (1779317):

scenario base wall head wall Δ wall [95% CI] requests
yarn-classic/hosted 472.5 ms 204.1 ms -56.2% [-59.5, -53.9] 187
yarn-classic/rescan 320.8 ms 143.6 ms -55.8% [-58.1, -53.7] 127
yarn-berry/hosted 225.1 ms 224.6 ms -0.9% ≈ 127
yarn-berry/rescan 187.6 ms 184.1 ms -1.0% ≈ 127

Against the commit before #1328 (f864148):

scenario base wall head wall Δ wall [95% CI] requests
yarn-classic/hosted 176.3 ms 208.9 ms +17.7% [+13.5, +22.5] 127 → 187
yarn-classic/rescan 137.1 ms 145.4 ms +3.5% [-5.4, +13.2] ≈ 127

Most of the remaining hosted delta comes from the 60 served-tarball downloads
(plus gunzip and hashing) that #1328 added deliberately, for the #558 sha1 and
the #591 dependency check. #1423 itself calls those downloads intentional, so
this PR leaves them in place. Callgrind on the hosted scan: total instructions
dropped from 1.74G to about 1.1G. yarn_classic_artifact_targets went from
68% inclusive to under 4%, and unlocked_among went from 29% to 0%.

Tests

issue test red → green
#1423 (target selection) hosted::engine::tests::classic_artifact_targets_read_the_lock_blocks_at_most_once counts block reads: 0 on a fully pinned rescan, exactly 1 for 200 unpinned deps fails (left: 1, right: 0) if the cheap pinned filter moves behind the block read. An earlier timing version of this test took 58 s with per-dep scans and 1.7 s with the fix
#1423 (target selection) quoted_fragment_urls_reads_each_resolved_url, classic_registry_copy_is_matched_by_block_name_and_source (updated to the set) new / existing
#1423 (#591 check) classic_deps::tests::unlocked_descriptors_in_matches_the_block_scan (the set form agrees with the block scan) new

Commands run locally:

  • cargo fmt --all -- --check: clean
  • cargo clippy --workspace --all-features -- -D warnings: clean
  • cargo test --workspace --all-features: 14229 passed, 13 failed. All 13
    also fail on main in this sandbox and none touch yarn code. Ten are
    permission-failure tests that can't fail as root (chmod is ignored), for
    example covgap_commands_vendor::*_state_write_failure_* and
    vendor::pypi_poetry::tests::wire_write_failure_*. The others need
    patches-api.socket.dev, which the proxy blocks, for example
    e2e_bun_lockb::binary_shared_bundled_record_hosted_pin_is_managed.
  • scripts/yarn-classic-vex-matrix.sh 1.22.22 1.0.2 (real yarn,
    SOCKET_PATCH_YARN_E2E_REQUIRED=1): exit 0, every suite passed (110 PASS
    cells)
  • cargo test -p socket-patch-cli --test scan --test hosted_memory_engine --test e2e_redirect_yarn_classic_build --test e2e_vendor_yarn_classic_build --test e2e_vendor_yarn_classic_dev_flow:
    all pass

Review notes (/code-review high, findings not fixed)

  • A grant for a package missing from the lock still forces one block read.
    The read is bounded at once per lock. The alternative was a whole-lock
    substring search per dep, which is what this PR removes.
  • yarn-berry's berry_lock_locks scans per dep too. That is a different
    backend and not part of this regression; the berry bench is unchanged. Left
    for a separate issue.

Follow-ups

  • The 60 served-tarball GETs on a first hosted scan run serially. To make them
    concurrent, they need the wheel window's design: a small cap, a first-attempt
    window, then serial resumption.

🤖 Generated with Claude Code

https://claude.ai/code/session_018G3cdPFQuuBbZahpQSKpoB


Generated by Claude Code

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@claude
Start fix for #1423
52dacec
Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Oct 12, 2026
Perf regression: yarn-classic/hosted wall +165% (5096c41a, #1328) #1423
Closed
Claude (claude) added 7 commits October 12, 2026 08:32
@claude
Read yarn.lock once when picking classic tarballs
d9cdf55
A hosted scan of a yarn classic project re-read the whole yarn.lock
once per patched package to decide which served tarballs it must
fetch. On a 3000-package lock with 60 patches that made hosted scans
about 2.6x slower than before #1328, even on a rescan that fetches
nothing.

The lock's registry copies are now collected in one pass, and the
cheap "lock already pins this artifact" test runs first, so a rescan
of a fully pinned lock never reads the blocks at all. The selected
tarballs are unchanged.

Fixes #1423

Assisted-by: Claude Code:claude-opus-5-5
@claude
Skip lock reads for absent names; count reads
6bb96fd
Keep the old quick "is this name in the lock at all" check before
the registry-copy lookup, so a grant for a package the lock never
mentions costs a substring search, not a read of every block. The
blocks now come from the shared memoized scan, which the rewriter
also reads later in the same run.

The regression test counts lock reads instead of timing them: none
on a fully pinned rescan, exactly one for many unpinned deps. It
fails if the cheap filters move back behind the block read.

Refs #1423

Assisted-by: Claude Code:claude-opus-5-5
@claude
Fetch yarn classic tarballs concurrently
ee84a85
A hosted scan of a yarn classic project downloads each patched
package's served tarball to check its sha1 and dependencies. Those
downloads ran one at a time, so 60 patches cost 60 round trips in a
row. They now run through the same ordered, bounded window the patch
record fetch uses, and the results are folded in the same order as
before, so pins, skips and --debug output are unchanged.

A new test serves three tarballs (slow, fast, missing) and checks
each pin carries its own tarball's sha1 and only the missing one is
skipped.

Refs #1423

Assisted-by: Claude Code:claude-opus-5-5
@claude
Check served yarn deps without re-splitting keys
286280b
The yarn classic dependency check (#591) runs once per patched
package and split every key of yarn.lock into its patterns each time,
even when the served package has no dependencies at all. On a large
lock this was the biggest remaining cost of a hosted scan.

It now returns at once when there is nothing to look up, and only
splits the keys whose text names one of the wanted packages. The
result is the same set of unlocked descriptors as before.

Refs #1423

Assisted-by: Claude Code:claude-opus-5-5
@claude
Look up pinned yarn artifacts in one lock pass
a60710e
Picking the served tarballs to fetch still searched the whole
yarn.lock twice per patched package: once for its pinned URL and once
for its name. The lock is now read once for every URL it pins with a
fragment, and each package is a set lookup.

Refs #1423

Assisted-by: Claude Code:claude-opus-5-5
@claude
Revert "Fetch yarn classic tarballs concurrently"
117e5c5
This reverts commit ee84a85.
@claude
Check served yarn deps against keys split once
9d0c1bc
The yarn classic dependency check (#591) runs once per patched
package and split every key of yarn.lock into its patterns each time.
On a large lock that was the biggest remaining cost of a hosted scan.
The rewriter already splits each key once, so the check now uses a
set built from those patterns, made the first time a package needs
it. The result is the same set of unlocked descriptors.

Target selection also goes back to a plain block scan (the shared
memo it used is not read by the hosted rewriter, so it only held a
copy of the lock), and builds its pinned-URL set only once a package
with a sha1 asks for it.

Refs #1423

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Oct 12, 2026
Swarm health manager log #1382
Open
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 12, 2026 10:14
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

cursor[bot] reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cursor[bot]
cursor Bot reviewed Oct 12, 2026
View reviewed changes

@cursor cursor Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 9d0c1bc. Configure here.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 12, 2026
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

Labeled Ready for review at head 9d0c1bc79c311694009d54f2e818b15c5836cd49.

  • CI: 36/36 run checks green, 50 path-skipped, no failures (no main-wide failures).
  • Bugbot: reviewed 9d0c1bc, no findings.
  • Mergeable, no conflicts; does not touch CHANGELOG.md.

Slack announcement not sent this run (Slack send unavailable); the next run will retry.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

Final review brief

What it does: Removes the 2.6x slowdown in yarn classic hosted scans that #1328 introduced (#1423). Two checks that re-read the whole yarn.lock once per patched package now build their lookup set once per scan, and only when they need it: the registry (name, version) copies, the #-pinned URLs, and the #591 locked-descriptor set.

Risk: low. It refactors the code into precomputed sets and keeps the same predicates (split_key_patterns / classic_key_real_name / classic_copy_source). Scoped packages, multi-descriptor keys, npm: aliases, CRLF locks and the selection order behave the same as before. The one case that changes in theory is an artifact URL that itself contains #, and serve URLs never do. The vendored path is unchanged.

Look here

  • engine.rs:1225-1252: select_classic_artifacts, the filter order and the lazy caches
  • engine.rs:1260-1273: quoted_fragment_urls, which replaces the per-dep lock.contains("\"{url}#")
  • engine.rs:1280-1291: classic_registry_copies
  • redirect/mod.rs:4013-4019:`` the descriptor set, built lazily from the already-split heads
  • classic_deps.rs:62-99:``` unlocked_descriptors_in

Verified

  • Read the full diff against the old code, edge case by edge case.
  • cargo test -p socket-patch-core --lib -- yarn hosted classic: 587 passed.
  • A new test asserts the lock blocks are read 0 times when every dep is pinned and exactly once for 200+ deps.
  • CHANGELOG.md is untouched and there's no debug code (the read counter is test-only).
  • CI: ci-ok and clippy are green, 86/86 check runs are passing or skipped, and Bugbot passed. No unresolved threads.

Changes I made: none.

Open questions (non-blocking):

  • The quoted_fragment_urls test could add a few cases: a closing quote right before a URL, a CRLF lock, and a URL in a comment line.
  • (String, String) is allocated on each registry-copy lookup.

Auto-merge is armed, so approving sends this straight to the merge queue.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko
Mikola Lysenko (mikolalysenko) enabled auto-merge October 12, 2026 12:10
Tanmay182003
Tanmay Singla (Tanmay182003) approved these changes Oct 12, 2026
View reviewed changes
@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Oct 12, 2026
Merged via the queue into main with commit 95b06dd Oct 12, 2026
86 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-yarn-classic-registry-copy-rescan branch October 12, 2026 16:11
This was referenced Oct 12, 2026
CI performance dashboard #1182
Open
chore(release): bump to 5.0.1 with release notes #1446
Closed
Fix hosted yarn classic rescan of v4 pins (#1462) #1463
Merged
github-merge-queue Bot pushed a commit that referenced this pull request Oct 13, 2026
@mikolalysenko @claude
chore(release): 5.0.1 (#1485)
2e0a6b9
* ci(release): publish npm from release.yml, not publish-npm.yml

Every socket-patch npm package's trusted publisher is registered against
release.yml, and npm allows only one publisher per package. The 5.0.0
release dispatched publish-npm.yml, whose OIDC token named that file, so
npm rejected it with E401 before staging anything.

Move the npm leg back inline as release.yml's npm-publish job (same steps
as publish-npm.yml) and delete publish-npm.yml, so the existing
registration keeps working. Add an `npm-retry-version` input that skips
the version gate, build, tag, GitHub release and crates.io jobs and
re-stages npm from an existing release's assets; this retries 5.0.0 now
and replaces the manual Publish npm dispatch. crates.io stays dispatched
through publish-cargo.yml, since crates.io allows several publishers per
crate.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* chore(release): fold 22 more PRs into the 5.0.0 notes

Cover the PRs merged to main after #1432's base (1779317..4cb3643):
Cargo source replacement and registry = "crates-io" (#338/#455, #386),
pnpm lockfile=false (#1074), berry removed descriptors (#1435), yarnrc
${VAR} reads (#1100), CRLF .npmrc append (#1196), Composer vendor-dir
cascade and global home (#439/#686, #586), Go +incompatible vex (#484),
gem moved BUNDLE_GEMFILE and commented Gemfile revert (#952, #988),
Poetry source = "pypi" (#1282) and Hatch path characters (#547).

No entries for #1411 (macOS fix to code #1393 added in this release;
the #1393 entry already describes the intended behavior), #1426 (perf
regression introduced and fixed within 5.0.0) or the CI/test/bench PRs.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* chore(release): cover #1419, #1434, #1440, #1442, #1444 in the 5.0.0 notes

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* chore(release): 5.0.1

Cut [Unreleased] into [5.0.1] — 2026-10-13 and stamp 5.0.1 with
scripts/version-sync.sh. The 5.0.1 notes cover main through 501c6d9
(#1449), i.e. every PR after the v5.0.0 tag (6ad6add): the npm
publish fix (5.0.0 never reached npm or crates.io, 5.0.1 is the first
v5 there), #1465, #759, #1460, #1462, #1445, #869, #1447, #1450,
#1467, and #1449 under Maintenance. CI/test-only PRs (#1448, #1452,
#1454, #1457, #1459) have no entries.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* chore(release): 5.0.1 notes for PRs merged since the cut

Covers #1469, #1470, #1474, #1475, #1478, #1479, #1480 and #1482; #1484 is
CI-only. The notes now cover main through 285a5e8.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@cursor cursor[bot] cursor[bot] left review comments
@Tanmay182003 Tanmay Singla (Tanmay182003) Tanmay182003 approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Perf regression: yarn-classic/hosted wall +165% (5096c41a, #1328)

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@mikolalysenko @Tanmay182003 @claude
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.