mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-24 16:46:06 -04:00
perf(pacquet/resolving-resolver-base): share ResolveResult.manifest via Arc
`ResolveResult` propagates by clone — once into `ResolvedPackage` in the deps-resolver's `TreeCtx`, again into each `DependenciesGraphNode` in the peer-resolved graph, and a few more times through the install dispatch. The `manifest` field is a `serde_json::Value` tree (per-version body) and every clone walked it node-by-node. Wrap it in `Arc` so `ResolveResult::clone()` bumps a refcount instead of deep-cloning the JSON tree: - New `SharedDependencyManifest = Arc<DependencyManifest>` alias on `pacquet-resolving-resolver-base`. - `ResolveResult::manifest` and `LatestInfo::latest_manifest` re-typed as `Option<SharedDependencyManifest>`. - All construction sites in npm / git / tarball / local / runtime resolvers and `resolve_from_workspace` wrap their built `Value` in `Arc::new`. - Read sites mostly worked unchanged via `Arc<Value>`'s `Deref` coercion. Two call sites that pass `Option<&Value>` to a helper (`manifest_unpacked_size`, `build_package_metadata`) switched to `.as_deref()`. Mirrors JS object-reference semantics — pnpm's `pkgResponse.manifest` is an object, not a deep copy, so propagating the response across resolve / install pays no copy. Tests: 295/295 in the resolver crates pass; 298/298 in `pacquet-package-manager`. Clippy clean.
This commit is contained in:
@@ -109,7 +109,7 @@ impl BunResolver {
|
||||
name_ver: None,
|
||||
latest: None,
|
||||
published_at: None,
|
||||
manifest: Some(manifest),
|
||||
manifest: Some(std::sync::Arc::new(manifest)),
|
||||
resolution,
|
||||
resolved_via: RESOLVED_VIA.to_string(),
|
||||
normalized_bare_specifier: Some(format!("runtime:{version_spec}")),
|
||||
@@ -157,10 +157,10 @@ impl BunResolver {
|
||||
return Ok(Some(LatestInfo::default()));
|
||||
};
|
||||
Ok(Some(LatestInfo {
|
||||
latest_manifest: Some(serde_json::json!({
|
||||
latest_manifest: Some(std::sync::Arc::new(serde_json::json!({
|
||||
"name": "bun",
|
||||
"version": name_ver.suffix.to_string(),
|
||||
})),
|
||||
}))),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ impl DenoResolver {
|
||||
name_ver: None,
|
||||
latest: None,
|
||||
published_at: None,
|
||||
manifest: Some(manifest),
|
||||
manifest: Some(std::sync::Arc::new(manifest)),
|
||||
resolution,
|
||||
resolved_via: RESOLVED_VIA.to_string(),
|
||||
normalized_bare_specifier: Some(format!("runtime:{version_spec}")),
|
||||
@@ -164,10 +164,10 @@ impl DenoResolver {
|
||||
return Ok(Some(LatestInfo::default()));
|
||||
};
|
||||
Ok(Some(LatestInfo {
|
||||
latest_manifest: Some(serde_json::json!({
|
||||
latest_manifest: Some(std::sync::Arc::new(serde_json::json!({
|
||||
"name": "deno",
|
||||
"version": name_ver.suffix.to_string(),
|
||||
})),
|
||||
}))),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ impl NodeResolver {
|
||||
name_ver: None,
|
||||
latest: None,
|
||||
published_at: None,
|
||||
manifest: Some(manifest),
|
||||
manifest: Some(std::sync::Arc::new(manifest)),
|
||||
resolution,
|
||||
resolved_via: RESOLVED_VIA.to_string(),
|
||||
normalized_bare_specifier: Some(format!("runtime:{range}")),
|
||||
@@ -193,10 +193,10 @@ impl NodeResolver {
|
||||
return Ok(Some(LatestInfo::default()));
|
||||
};
|
||||
Ok(Some(LatestInfo {
|
||||
latest_manifest: Some(serde_json::json!({
|
||||
latest_manifest: Some(std::sync::Arc::new(serde_json::json!({
|
||||
"name": "node",
|
||||
"version": version,
|
||||
})),
|
||||
}))),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ fn build_packages_and_snapshots(
|
||||
/// `dependencies` / `optionalDependencies` / `transitivePeerDependencies` /
|
||||
/// `optional` / `patched`, which go on the snapshot below).
|
||||
fn build_package_metadata(node: &DependenciesGraphNode) -> PackageMetadata {
|
||||
let manifest = node.resolve_result.manifest.as_ref();
|
||||
let manifest = node.resolve_result.manifest.as_deref();
|
||||
|
||||
let engines = manifest
|
||||
.and_then(|m| m.get("engines"))
|
||||
|
||||
@@ -33,7 +33,7 @@ fn make_resolve_result(name: &str, version: &str, manifest: serde_json::Value) -
|
||||
name_ver: Some(name_ver),
|
||||
latest: None,
|
||||
published_at: None,
|
||||
manifest: Some(manifest),
|
||||
manifest: Some(std::sync::Arc::new(manifest)),
|
||||
resolution: make_registry_resolution(),
|
||||
resolved_via: "npm-registry".to_string(),
|
||||
normalized_bare_specifier: None,
|
||||
|
||||
@@ -148,7 +148,7 @@ impl<'a> InstallPackageFromRegistry<'a> {
|
||||
|
||||
if first_visit {
|
||||
let (tarball_url, integrity) = extract_tarball(&resolution.resolution)?;
|
||||
let unpacked_size = manifest_unpacked_size(resolution.manifest.as_ref());
|
||||
let unpacked_size = manifest_unpacked_size(resolution.manifest.as_deref());
|
||||
|
||||
// `pnpm:progress resolved` mirrors pnpm's emit at
|
||||
// <https://github.com/pnpm/pnpm/blob/086c5e91e8/installing/deps-resolver/src/resolveDependencies.ts#L1586>:
|
||||
|
||||
@@ -52,7 +52,7 @@ fn fake_result(name: &str, version: &str, manifest: serde_json::Value) -> Resolv
|
||||
name_ver: Some(name_ver),
|
||||
latest: Some(version.to_string()),
|
||||
published_at: None,
|
||||
manifest: Some(manifest),
|
||||
manifest: Some(std::sync::Arc::new(manifest)),
|
||||
resolution: LockfileResolution::Tarball(TarballResolution {
|
||||
tarball: format!("https://registry.example/{name}-{version}.tgz"),
|
||||
integrity: None,
|
||||
|
||||
@@ -53,7 +53,7 @@ fn fake_result(name: &str, version: &str, manifest: serde_json::Value) -> Resolv
|
||||
name_ver: Some(name_ver),
|
||||
latest: Some(version.to_string()),
|
||||
published_at: None,
|
||||
manifest: Some(manifest),
|
||||
manifest: Some(std::sync::Arc::new(manifest)),
|
||||
resolution: LockfileResolution::Tarball(TarballResolution {
|
||||
tarball: format!("https://registry.example/{name}-{version}.tgz"),
|
||||
integrity: None,
|
||||
|
||||
@@ -76,7 +76,7 @@ pub enum LocalResolverUpdate {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocalResolveResult {
|
||||
pub id: PkgResolutionId,
|
||||
pub manifest: Option<serde_json::Value>,
|
||||
pub manifest: Option<std::sync::Arc<serde_json::Value>>,
|
||||
pub normalized_bare_specifier: Option<String>,
|
||||
pub resolution: LockfileResolution,
|
||||
/// `local-filesystem` — the same `resolvedVia` tag upstream uses
|
||||
@@ -263,7 +263,7 @@ async fn resolve_spec(
|
||||
|
||||
Ok(Some(LocalResolveResult {
|
||||
id: spec.id.clone(),
|
||||
manifest: Some(manifest),
|
||||
manifest: Some(std::sync::Arc::new(manifest)),
|
||||
normalized_bare_specifier: Some(spec.normalized_bare_specifier),
|
||||
resolution: LockfileResolution::Directory(DirectoryResolution {
|
||||
directory: spec.dependency_path,
|
||||
|
||||
@@ -405,7 +405,9 @@ pub(crate) fn build_resolve_result(
|
||||
path: None,
|
||||
});
|
||||
let published_at = meta.published_at(&picked.version.to_string()).map(str::to_string);
|
||||
let manifest = Some(serde_json::to_value(picked).map_err(|err| Box::new(err) as ResolveError)?);
|
||||
let manifest = Some(std::sync::Arc::new(
|
||||
serde_json::to_value(picked).map_err(|err| Box::new(err) as ResolveError)?,
|
||||
));
|
||||
let policy_violation = detect_min_release_age_violation(
|
||||
&pkg_name,
|
||||
&picked.version.to_string(),
|
||||
|
||||
@@ -266,7 +266,7 @@ fn resolve_from_local_package(
|
||||
name_ver: None,
|
||||
latest: None,
|
||||
published_at: None,
|
||||
manifest: Some(local_package.manifest.clone()),
|
||||
manifest: Some(std::sync::Arc::new(local_package.manifest.clone())),
|
||||
resolution: LockfileResolution::Directory(DirectoryResolution { directory }),
|
||||
resolved_via: "workspace".to_string(),
|
||||
normalized_bare_specifier: None,
|
||||
|
||||
@@ -28,9 +28,10 @@ mod verifier;
|
||||
pub use resolve::{
|
||||
DIRECT_DEP_SELECTOR_WEIGHT, DependencyManifest, EXISTING_VERSION_SELECTOR_WEIGHT, LatestInfo,
|
||||
LatestQuery, PkgResolutionId, PreferredVersions, ResolveError, ResolveFuture,
|
||||
ResolveLatestFuture, ResolveOptions, ResolveResult, Resolver, UpdateBehavior,
|
||||
VersionSelectorEntry, VersionSelectorType, VersionSelectorWithWeight, VersionSelectors,
|
||||
WantedDependency, WorkspacePackage, WorkspacePackages, WorkspacePackagesByVersion,
|
||||
ResolveLatestFuture, ResolveOptions, ResolveResult, Resolver, SharedDependencyManifest,
|
||||
UpdateBehavior, VersionSelectorEntry, VersionSelectorType, VersionSelectorWithWeight,
|
||||
VersionSelectors, WantedDependency, WorkspacePackage, WorkspacePackages,
|
||||
WorkspacePackagesByVersion,
|
||||
};
|
||||
pub use verifier::{
|
||||
ResolutionPolicyViolation, ResolutionVerification, ResolutionVerifier, VerifyCtx, VerifyFuture,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//! pnpm's
|
||||
//! [`createResolver`](https://github.com/pnpm/pnpm/blob/3687b0e180/resolving/default-resolver/src/index.ts#L97-L173).
|
||||
|
||||
use std::{collections::BTreeMap, future::Future, path::PathBuf, pin::Pin};
|
||||
use std::{collections::BTreeMap, future::Future, path::PathBuf, pin::Pin, sync::Arc};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use derive_more::{Display, From};
|
||||
@@ -236,6 +236,16 @@ pub struct ResolveOptions {
|
||||
/// in-memory manifest lands, swap this alias for it.
|
||||
pub type DependencyManifest = serde_json::Value;
|
||||
|
||||
/// `Arc`-shared variant of [`DependencyManifest`], used in
|
||||
/// [`ResolveResult::manifest`]. Wrapping the manifest avoids the
|
||||
/// deep-clone of the JSON tree every time a `ResolveResult`
|
||||
/// propagates — the deps-resolver stores one copy in
|
||||
/// `ResolvedPackage` and another in each `DependenciesGraph` node,
|
||||
/// each `Clone` cost dropped from O(manifest size) to a refcount
|
||||
/// bump. Mirrors JS object-reference semantics — pnpm's
|
||||
/// `resolveResult.manifest` is an object, not a deep copy.
|
||||
pub type SharedDependencyManifest = Arc<DependencyManifest>;
|
||||
|
||||
/// Outcome of one [`Resolver::resolve`] call when the resolver claims
|
||||
/// the wanted dependency. Mirrors pnpm's
|
||||
/// [`ResolveResult`](https://github.com/pnpm/pnpm/blob/3687b0e180/resolving/resolver-base/src/index.ts#L212-L237).
|
||||
@@ -261,7 +271,10 @@ pub struct ResolveResult {
|
||||
pub published_at: Option<String>,
|
||||
/// The manifest fragment the resolver fetched. Optional because
|
||||
/// some protocols defer manifest reading to the fetch step.
|
||||
pub manifest: Option<DependencyManifest>,
|
||||
/// Held as [`SharedDependencyManifest`] (`Arc`-shared) so the
|
||||
/// deps-resolver's tree walk and the per-snapshot graph copies
|
||||
/// don't deep-clone the JSON tree per occurrence.
|
||||
pub manifest: Option<SharedDependencyManifest>,
|
||||
/// Where the artifact lives. Pacquet reuses
|
||||
/// [`LockfileResolution`] for this — same shape as upstream's
|
||||
/// `Resolution`, which is the discriminated union over
|
||||
@@ -309,7 +322,7 @@ pub struct LatestQuery {
|
||||
/// (`Ok(Some(LatestInfo { latest_manifest: None }))`).
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct LatestInfo {
|
||||
pub latest_manifest: Option<DependencyManifest>,
|
||||
pub latest_manifest: Option<SharedDependencyManifest>,
|
||||
}
|
||||
|
||||
/// Error type the resolver seam uses. Boxed-trait-object today so each
|
||||
|
||||
Reference in New Issue
Block a user