Skip to content

refactor(pm): pipeline deps resolver - #2576

Merged
elrrrrrrr merged 8 commits into
nextfrom
feat/preload-tgz-pipeline
Feb 11, 2026
Merged

elrrrrrrr merged 8 commits into
nextfrom
feat/preload-tgz-pipeline

Conversation

@elrrrrrrr

@elrrrrrrr elrrrrrrr commented Feb 4, 2026 •

Copy link
Copy Markdown
Contributor
  • Implement pipeline architecture using oncemap for manifest parsing, TGZ download, extraction, and file cloning.
  • use libdeflater for decompression and rayon for parallel file writing.
  • Remove tokio-runtime spawn_blocking limitations.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @elrrrrrrr, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the dependency resolution and installation process within the utoo-pm crate. The core change is the implementation of a pipeline architecture that enables highly concurrent operations for fetching package manifests, downloading tarballs, and cloning packages into the node_modules directory. This design aims to drastically improve the speed and efficiency of utoo install commands by parallelizing tasks that were previously more sequential. The refactor also includes updates to dependency versions and the adoption of more performant compression libraries.

Highlights

  • Pipeline Architecture: Implemented a new pipeline for dependency resolution and installation, allowing concurrent manifest fetching, tarball downloading, and package cloning. This design is inspired by Bun's approach to speed up utoo install operations.
  • Concurrency Control & Deduplication: Introduced global OnceMap instances and semaphores to manage and deduplicate concurrent download and clone operations, ensuring efficiency and preventing redundant work across different stages of the pipeline.
  • Performance Optimizations: Switched from stream-based tarball unpacking to the libdeflater library for Gzip decompression, which is expected to offer significant performance improvements during package extraction.
  • Dependency Management Refinements: Updated Cargo.lock and Cargo.toml to reflect new dependencies (libdeflater, bytes) and removed/updated others (async-compression, tokio-tar, redox_syscall), aligning with the new pipeline's requirements.
  • Simplified Tokio Runtime: The main function now leverages the #[tokio::main] macro for simpler and more idiomatic Tokio runtime initialization, reducing boilerplate code.
Changelog
  • Cargo.lock
    • Updated async-compression from 0.3.15 to 0.4.32.
    • Updated bytes from 1.10.1 to 1.11.1.
    • Added new dependencies libdeflate-sys and libdeflater (version 1.25.0).
    • Removed tokio-tar dependency.
    • Adjusted redox_syscall dependency references.
  • crates/pm/Cargo.toml
    • Removed explicit version and features for async-compression.
    • Added bytes = "1.11.0".
    • Added libdeflater = "1.25".
    • Removed tokio-tar = "0.3.1".
    • Removed features = ["io"] from tokio-util.
  • crates/pm/src/cmd/deps.rs
    • Integrated PipelineInstaller to start and await pipeline workers during dependency building.
  • crates/pm/src/cmd/rebuild.rs
    • Modified rebuild to await pipeline workers if they were initiated during ensure_package_lock.
  • crates/pm/src/helper/fs.rs
    • Updated BuildDepsOptions to use PipelineReceiver for progress events and to return PipelineChannels for worker management.
  • crates/pm/src/helper/lock.rs
    • Introduced BuildDepsResult to encapsulate PackageLock and optional PipelineHandles.
    • Replaced build_deps_with_download with build_deps_with_pipeline to leverage the new pipeline.
  • crates/pm/src/main.rs
    • Switched to #[tokio::main] macro for simplified Tokio runtime initialization.
  • crates/pm/src/service/install.rs
    • Removed manual semaphore-based concurrency control, now handled by the pipeline.
    • Updated install_packages to use clone_package_once from the pipeline service.
    • Ensures pipeline workers complete after package installation.
  • crates/pm/src/service/mod.rs
    • Added pipeline module.
  • crates/pm/src/service/pipeline.rs
    • New file introducing the core pipeline logic, including PipelineStats, OnceMap for caching, semaphores for concurrency, and workers for concurrent download and clone operations.
  • crates/pm/src/util/cloner.rs
    • Refactored Linux cloning logic to a phased approach (collect, create dirs, clone files) for better control and performance.
  • crates/pm/src/util/config.rs
    • Added get_manifests_concurrency_limit_sync for synchronous access to concurrency settings.
  • crates/pm/src/util/downloader.rs
    • Replaced DashMap based locking with OnceMap for download deduplication.
    • Introduced download_bytes and extract_and_write functions.
    • Switched to libdeflater for Gzip decompression, improving performance.
  • crates/pm/src/util/oncemap.rs
    • Added wait_if_pending method to OnceMap to allow tasks to wait for in-progress computations.
  • crates/ruborist/src/lib.rs
    • Exported PackageTarballInfo for use in progress events.
  • crates/ruborist/src/resolver/builder.rs
    • Modified BFS phase to emit PackagePlaced events for pipeline cloning, including parent path and depth information.
    • Updated BuildEvent parameters to use &str for efficiency.
  • crates/ruborist/src/resolver/preload.rs
    • Increased DEFAULT_CONCURRENCY for manifest fetching to 64.
    • Emits PackageResolved events during preloading to trigger early tarball downloads.
    • Updated BuildEvent parameters to use &str.
  • crates/ruborist/src/traits/progress.rs
    • Introduced PackageTarballInfo and new BuildEvent::PackageResolved and BuildEvent::PackagePlaced events.
    • Refactored BuildEvent and EventReceiver to use references (&'a str, &'a Path) for improved performance and reduced allocations.
Activity
  • The pull request was opened by elrrrrrrr with the title "refactor(pm): pipeline deps resolver", indicating a significant refactoring effort.
  • The changes introduce a new pipeline service to parallelize dependency resolution, downloading, and cloning, aiming to improve installation speed.
  • Updates to Cargo.toml and Cargo.lock reflect the introduction of new dependencies like libdeflater and changes in existing ones to support the new architecture.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a significant and well-executed refactoring to implement a pipelined dependency installation process, which should greatly improve performance by parallelizing downloads and installations. The introduction of the pipeline service and the use of OnceMap for deduplicating work are excellent changes. However, I have identified a few areas for improvement. The switch to #[tokio::main] removes some potentially important runtime performance tuning. Additionally, the new download and extraction logic in downloader.rs has moved from a streaming to an in-memory model, which could cause high memory usage and potential OOM errors for large packages. I've also suggested a minor structural improvement in the same file to combine blocking operations.

Comment thread crates/pm/src/main.rs Resolved
Comment thread crates/pm/src/util/downloader.rs Resolved
Comment thread crates/pm/src/util/downloader.rs Resolved
@elrrrrrrr
elrrrrrrr force-pushed the feat/preload-tgz-pipeline branch 2 times, most recently from 78745c2 to 8ad00c2 Compare February 9, 2026 02:40
@elrrrrrrr elrrrrrrr added the A-Pkg Manager Area: Package Manager label Feb 9, 2026
@elrrrrrrr
elrrrrrrr force-pushed the feat/preload-tgz-pipeline branch from 2f66e20 to 70be474 Compare February 9, 2026 09:46
@elrrrrrrr
elrrrrrrr force-pushed the feat/preload-tgz-pipeline branch from 70be474 to 48350dd Compare February 9, 2026 09:52
@elrrrrrrr
elrrrrrrr marked this pull request as ready for review February 10, 2026 04:04
fireairforce
fireairforce previously approved these changes Feb 10, 2026
Comment thread crates/pm/src/service/pipeline/mod.rs Resolved
Comment thread crates/pm/src/service/clean.rs Resolved
Comment thread crates/pm/src/service/clean.rs Resolved
Comment thread crates/pm/src/util/extractor.rs Resolved
Comment thread crates/ruborist/src/traits/progress.rs Resolved

Copilot AI commented Feb 10, 2026

Copy link
Copy Markdown
Contributor

@xusd320 I've opened a new pull request, #2581, to work on those changes. Once the pull request is ready, I'll request review from you.

@xusd320
xusd320 enabled auto-merge (squash) February 10, 2026 10:13
@elrrrrrrr
elrrrrrrr disabled auto-merge February 11, 2026 03:27
@elrrrrrrr
elrrrrrrr enabled auto-merge (squash) February 11, 2026 03:28
@elrrrrrrr
elrrrrrrr disabled auto-merge February 11, 2026 06:15
@elrrrrrrr
elrrrrrrr merged commit ba90943 into next Feb 11, 2026
18 checks passed
@elrrrrrrr
elrrrrrrr deleted the feat/preload-tgz-pipeline branch February 11, 2026 06:15
elrrrrrrr added a commit that referenced this pull request Apr 16, 2026
#2576 replaced `ensure_package_lock(root_path)` with a bare
`fs::try_exists(&lock_path)` check in Self::install. As a result any
`ut install` with an existing package-lock.json skipped the manifest
resolution entirely — even when package.json had been edited — silently
installing the stale tree from the lockfile.

Re-introduce the freshness check: when the lockfile exists we now still
run `is_pkg_lock_outdated`. Stale lockfiles (or freshness-check errors
— corrupt lockfiles etc.) fall through to the pipeline cold path so we
keep the concurrent resolve + download benefit #2576 was built for.

Regression guard lives in `e2e/pm/stale-lockfile/` — a fixture that
commits a package-lock.json whose root `dependencies` is empty while
package.json declares abbrev and ini. Case 8.2 in e2e/utoo-pm.sh
asserts:
  - `ut install` regenerates the lockfile
  - both packages end up in node_modules/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
elrrrrrrr added a commit that referenced this pull request Apr 16, 2026
`fs::try_exists(&lock_path)` check in Self::install. As a result any
`ut install` with an existing package-lock.json skipped the manifest
resolution entirely — even when package.json had been edited — silently
installing the stale tree from the lockfile.

Re-introduce the freshness check: when the lockfile exists we now still
run `is_pkg_lock_outdated`. Stale lockfiles (or freshness-check errors
— corrupt lockfiles etc.) fall through to the pipeline cold path so we
keep the concurrent resolve + download benefit #2576 was built for.

Regression guard lives in `e2e/pm/stale-lockfile/` — a fixture that
commits a package-lock.json whose root `dependencies` is empty while
package.json declares abbrev and ini. Case 8.2 in e2e/utoo-pm.sh
asserts:
  - `ut install` regenerates the lockfile
  - both packages end up in node_modules/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fireairforce pushed a commit that referenced this pull request Apr 16, 2026
* fix(pm): detect stale lockfile in ut install

`fs::try_exists(&lock_path)` check in Self::install. As a result any
`ut install` with an existing package-lock.json skipped the manifest
resolution entirely — even when package.json had been edited — silently
installing the stale tree from the lockfile.

Re-introduce the freshness check: when the lockfile exists we now still
run `is_pkg_lock_outdated`. Stale lockfiles (or freshness-check errors
— corrupt lockfiles etc.) fall through to the pipeline cold path so we
keep the concurrent resolve + download benefit #2576 was built for.

Regression guard lives in `e2e/pm/stale-lockfile/` — a fixture that
commits a package-lock.json whose root `dependencies` is empty while
package.json declares abbrev and ini. Case 8.2 in e2e/utoo-pm.sh
asserts:
  - `ut install` regenerates the lockfile
  - both packages end up in node_modules/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(pm): simplify stale-lockfile detection per review

- Drop duplicate `tracing::warn` in Self::install; `is_pkg_lock_outdated`
  already logs the specific mismatch reason (root deps changed, engines
  changed, …). Users now see one warn instead of two per stale detection.
- Collapse `lock_exists` + `lock_stale` into a single `use_fresh_lock`
  expression — removes the double-negation.
- Drop the committed stale `package-lock.json` fixture and the
  `git checkout --` dance in Case 8.2. The e2e now writes the stale
  lockfile inline via heredoc, which:
    * survives local edits / worktrees / stashed state
    * avoids the `git add -f` fight with .gitignore
    * keeps the fixture dir to just package.json (the declarative half)
- Assert root deps via `node` inspection of `packages[""].dependencies`
  instead of `grep "abbrev"`, which would match transitive
  `node_modules/abbrev` entries and hide a regression where only the
  tree — not the root declaration — got rewritten.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* perf(pm): reuse cached package.json in is_pkg_lock_outdated

The freshness check was reading the root package.json three times per
`ut install` — once as DepsView, once inside find_workspaces (for the
"workspaces" field), once as EnginesView. Each workspace was also read
twice (once by find_workspaces, again as DepsView).

Load the root as full `PackageJson` via `get_or_load_package_json` so
subsequent reads on the same path hit the in-process cache (filled by
`update_package_json`'s write-through). Within this function, drop the
DepsView/EnginesView intermediates and read the fields directly off the
same `PackageJson`; workspaces come back pre-loaded on the
`find_workspaces` tuple so we don't re-load them either.

Cache coherence: `update_package_json` already calls `set_package_json`
write-through after every edit, and every `ut install` starts a fresh
process with a cold cache — so external edits (IDE, `git checkout`)
still see a disk read next run.

Tests: the original `test_is_pkg_lock_outdated` simulated a
"write file, re-check" flow that doesn't happen in real invocations
(every mutation goes via `update_package_json` + write-through). Split
it into five independent scenarios, each with its own TempDir so cache
keys don't collide, which is also the idiomatic "one test per case".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Pkg Manager Area: Package Manager

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants