Repository navigation
Fix slow hosted Poetry lock rewrite (#1473) - #1475
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A hosted scan that pins N packages in one poetry.lock or pdm.lock searched the whole lock text two or three times per changed fragment to locate it and check it is unique, and the Poetry redirect then parsed the whole lock once more just to read [metadata] lock-version for the stale-install warning. On the 400-package bench those passes were about a fifth of the scan's instructions. The batch now finds and counts every changed fragment in one Aho-Corasick pass over the original and one over the result, and hands the lock version it read from its own parse back to the Poetry redirect. Output is unchanged: the batch-vs-step equivalence tests still pass, and a self-overlapping fragment only ever sends the batch to the step-by-step path, never accepts what that path would refuse. Refs #1473 Assisted-by: Claude Code:claude-opus-5-5
Building one Aho-Corasick automaton over whole package units cost more than the per-fragment scans it replaced. Search for each fragment's first 64 bytes instead and compare the full fragment at each hit: the same single pass, with an automaton a few KB in size. On the 400-package poetry/hosted bench the scan now runs 403M instructions instead of 506M on main, with a byte-identical lock and report. Refs #1473 Assisted-by: Claude Code:claude-opus-5-5
Say exactly which sites the test-only parse and scan counters instrument, count renders through the same helper, note that only [metadata] lock-version is untouched by package rewrites, and stop the Poetry redirect's local shadowing the lock_version function. No behavior change. Refs #1473 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 bbb3185. Configure here.
|
Labeled Ready for review by the burn-down agent.
Slack announcement not sent this run (Slack send tool unavailable); the next run will retry. Generated by Claude Code |
Final review briefWhat it does: The batched Poetry/PDM lock rewrite used to search the whole lock text 2-3 times for every changed fragment. It now finds all fragments in one pass over the original and one over the result, using an Aho-Corasick automaton over each fragment's first 64 bytes and comparing the full fragment at each hit. The Poetry redirect also stops re-parsing the rewritten lock just to read Risk: low. The new search can only refuse more often than the old one (it counts overlapping matches). A refused batch falls back to the step-by-step path, which is the reference result, and an accepted batch must still match a full re-render byte for byte. The new counters are Look here
Verified
Changes I made: none. Open questions: none. #1473 stays open for the VEX-parse follow-up the description lists. Auto-merge is armed, so approving sends this straight to the merge queue. Generated by Claude Code |
* 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) <noreply@anthropic.com> * 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) <noreply@anthropic.com> * chore(release): cover #1419, #1434, #1440, #1442, #1444 in the 5.0.0 notes Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * 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) <noreply@anthropic.com> * 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) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LLM Description written by Claude Code:claude-opus-5-5
Refs #1473. This is the rewrite half of the issue. The VEX half is left for a follow-up, see below.
Summary
A hosted
scanof a Poetry or PDM project rewrites every patchedpackage in
poetry.lock/pdm.lockin one batch(
utils::lock_fragments::rewrite_batch). This PR cuts the work thatbatch and the Poetry redirect repeat once per package:
per changed fragment:
text.matches(old).count()andtext.find(old)on the original, then
result.matches(new).count()on the result.It now finds every fragment in one pass over the original and one
over the result (
sole_occurrences). That pass is an Aho-Corasick searchfor each fragment's first 64 bytes, with the full fragment compared at
each hit. A fragment that isn't there exactly once still ends the batch.
patch::redirect::poetry::pre_1_4_writerparsedthe whole rewritten lock just to read
[metadata] lock-version. Thebatch now hands back the version it read from its own parse
(
rewrite_poetry_lock_all_with_version). Only the step-by-step fallbackstill parses for it.
Lock output is unchanged. Using overlapping counts in
sole_occurrencesonly rejects more: a self-overlapping fragment sends the batch to the
step-by-step path, which is the reference result. The batch is never
accepted where that path would refuse.
Evidence
Callgrind,
poetry/hostedbench scenario (400 packages, 12 patched), same fixture:rewrite_batchrewrite_poetry15c2c83The rewritten
poetry.lockand the--jsonreport are byte-identical tomain's (ports and paths normalized). On the 4-vCPU sandbox, wall-clock
socket-patch-bench comparestayed inside its noise band (poetry/rescan−7.2% [−11.8, −2.8], poetry/hosted −1.8% [−5.6, +4.1]). The first version of the
change built the automaton over whole fragments, which cost more than it
saved. The second commit fixes that.
Regression tests (red on main, green here):
patch::redirect::poetry::equivalence_tests::many_patches_render_the_lock_oncenow also asserts 2 whole-lock parses and 2 whole-lock scans for 12
patched packages. On main it fails with
PARSES left: 3, right: 2.patch::redirect::pdm::parse_reuse_equivalence_tests::many_patches_render_the_lock_onceasserts 2 scans. On main it fails with
SCANS left: 144, right: 2.utils::lock_fragments::tests::sole_occurrences_finds_each_needle_once_or_refusescovers shared 64+ byte prefixes, missing, repeated, empty and
self-overlapping needles.
utils::poetry_lockbatch-vs-steps equivalence now also checks the versionthe batch reads equals the output lock's
lock_version.Local checks (head
bbb3185):cargo fmt --all -- --check: OKcargo clippy --workspace --all-features -- -D warnings: OKcargo test --workspace --all-features --no-fail-fast: 219 test binaries pass. 13 tests fail, and none of them is in the Poetry/PDM rewrite path:This sandbox runs as uid 0, which ignores those permissions
(
covgap_commands_vendor×3,in_process_redirect×3,repair×2,core
copy_tree/vlt_heal/pypi_poetry/pypi_requirementswire-failure tests).e2e_bun_lockb::binary_shared_bundled_record_hosted_pin_is_managedneeds
patches-api.socket.dev, which the sandbox proxy blocks.e2e_vex_build -- poetry:: --ignoredwith real Poetry 2.4.3: 4/4 pass.e2e_vex_build -- pdm:: --ignoredwith real PDM 2.29.2 (SOCKET_PATCH_PDM_E2E_REQUIRED=1): 2/2 pass.npm/,pypi/,gem/don't touch this code)./code-review high: no correctness findings. Fixed inbbb3185: thecounter docs now say exactly which sites they instrument, renders are
counted through the same
tickhelper, the "[metadata]untouched" doc isnarrowed to
lock-version, and the shadowing local is renamed. Not changed:LockBatchfield.LockBatchis shared with PDM, which has no suchfield.
rewrite_poetry_lock_allis kept as the version-less entry point theequivalence tests and docs use.
start with their
namekey within that window.AhoCorasick::newfailing only sends the batch step by step.uses. Squash-merge drops it.
Not in this PR (follow-up for #1473)
vex::discover::pypi_locks::extractis now ~35% of the scan's instructions.Both VEX discovery passes (before and after the rewrite) parse
poetry.lockinto a
DocumentMutagain. Sharing the rewrite's parse with VEX, orreading it as a spanned
Document, means changing the uv/pylock/PDM VEXhelpers that take
&DocumentMut. That is a separate change, so #1473stays open for it.
🤖 Generated with Claude Code
https://claude.ai/code/session_01UELYzooW2PT3YpQweak8tS
Generated by Claude Code