Skip to content

perf(pacquet/install): optimistic_repeat_install fails to fire under vlt's lockfile+node_modules bench #11992

Description

@zkochan

Problem

The 2026-05-27 benchmarks.vlt.sh chart (pacquet 0.2.12 vs pnpm 11.3.0) shows the lockfile+node_modules row still 1.92–9.09× slower than pnpm across next, astro, vue, large, babylon, and the same family hits cache+lockfile+node_modules × large = 1.57× and × babylon = 2.87×. Per the tracker comment, pacquet 0.2.12 includes the optimistic_repeat_install port (#11943) and the workspace-state-fix (#11946), so the fast path should fire here.

Locally on the same fixture set, optimistic_repeat_install does fire — post-#11946 local re-bench had next × lockfile+node_modules = 0.07× and the babylon × *+node_modules cells at 0.07–0.17×. Something about vlt's exact scenario keeps the fast path from firing.

What lockfile+node_modules.sh actually does

From vltpkg/benchmarks/scripts/variations/lockfile+node_modules.sh:

  • --setup="bash …/clean-helpers.sh clean_all" — full wipe once, before all pkg managers
  • --prepare="sleep 1; bash …/clean-helpers.sh clean_all_cache clean_package_manager_files" — wipes caches + transient pkg-manager files between each iteration; node_modules and pnpm-lock.yaml carry over
  • --cleanup="bash …/clean-helpers.sh clean_all" — full wipe after all pkg managers

clean_pacquet_cache removes ~/.cache/pnpm and ~/.local/share/pnpm/store. The workspace state file lives at node_modules/.pnpm-workspace-state-v1.json, which is not in the cache, so it should survive a prepare cycle.

Hypotheses

  1. clean_pacquet_cache wipes something the optimistic check depends on. The check itself reads node_modules/.pnpm-workspace-state-v1.json and package.json mtimes — neither lives under ~/.cache/pnpm. But pacquet_store_dir::register_project writes into the store registry, which lives at ~/.local/share/pnpm/store/<version>/registry.json (or similar) — wiped between iterations. If the optimistic check transitively touches that, it might fail.
  2. First-iteration cross-PM contamination. hyperfine runs each pkg manager's warmup+runs cycle in sequence, so pacquet's first warmup iteration sees a node_modules written by the previous pkg manager. .pnpm-workspace-state-v1.json is either pnpm-format (potentially incompatible field set) or missing (bun/vlt didn't write it). Pacquet's first iteration falls through to full install, then writes its own state. Iteration 2+ should match. With BENCH_WARMUP=1, BENCH_RUNS=3, the mean of 3 fast iterations should still be near pnpm.
  3. mtime mismatch on the largest fixtures. babylon × lockfile+node_modules = 9.09× is the worst cell; the workspace state file has 87 importers. If any importer's package.json mtime is touched by clean_package_manager_files/clean_all_cache between iterations, the optimistic check rejects.
  4. Settings comparison fails. current_settings (in pacquet/crates/package-manager/src/optimistic_repeat_install.rs) builds the settings record from config + node_linker + included. If vlt's invocation environment differs from what was recorded in the state file (e.g. a default that changed between pacquet versions inside the iteration), the comparison fails every time.

Reproduction

Clone vltpkg/benchmarks, install pacquet@0.2.12 and pnpm@11.3.0, then run only the cell that's worst (babylon × lockfile+node_modules):

cd benchmarks
git clone https://github.com/babel/babel.git fixtures/babylon
# Run the variation in isolation
BENCH_INCLUDE_PACQUET=1 BENCH_INCLUDE_PNPM=1 BENCH_WARMUP=1 BENCH_RUNS=3 \
  RUST_LOG=pacquet=debug \
  bash scripts/variations/lockfile+node_modules.sh fixtures/babylon

Grep the captured pacquet-output-*.log files for optimistic_repeat_install decisions. Each measured iteration should log either UpToDate (fast path) or a Skipped { reason: ... } with the rejection cause.

Fix shape

Falls out of the diagnosis. If (1), narrow clean_pacquet_cache upstream in vlt's bench. If (2)/(3), the fix is in pacquet (mtime comparison tolerance, or accept a foreign-written state file when the settings record is compatible). If (4), align current_settings with what gets written.

Scope

Covers the lockfile+node_modules row (next/astro/svelte/vue/large/babylon) and cache+lockfile+node_modules × {large, babylon} cells. Once it fires reliably, all eight should drop to 0.05–0.20× pnpm, matching the smaller fixtures.

Tracker: #11902


Written by an agent (Claude Code, claude-opus-4-7).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions