Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions crates/pm/src/util/user_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,22 @@ pub fn get_install_scope() -> InstallScope {
INSTALL_SCOPE.get().copied().unwrap_or_default()
}

// Manifest fetch concurrency configuration
// Manifest fetch concurrency configuration.
//
// Sweep history under the worker-pool architecture:
// cap=96 spawn_blocking parse: wall=2.23s avg_conc=66
// cap=128 spawn_blocking parse: wall=2.15s avg_conc=84
// cap=160 spawn_blocking parse: wall=1.90s avg_conc=81 (parse queue p95=200ms)
// cap=160 inline parse : wall=2.14s avg_conc=119 (parse queue=0ms)
//
// Inline parse eliminated blocking-pool queue but cap=160 lifted
// concurrency past the per-source Cloudflare throttle, inflating
// per-req from 55 ms → 93 ms. Net wall flat. cap=128 + inline parse
// should land near the sweet spot: enough concurrency to saturate
// independent connections, low enough to stay under Cloudflare's
// per-source throttle threshold.
static MANIFESTS_CONCURRENCY_LIMIT: LazyLock<ConfigValue<usize>> =
LazyLock::new(|| ConfigValue::new("manifests-concurrency-limit", 64));
LazyLock::new(|| ConfigValue::new("manifests-concurrency-limit", 128));

pub fn set_manifests_concurrency_limit(value: Option<usize>) {
MANIFESTS_CONCURRENCY_LIMIT.set(value);
Expand Down
91 changes: 70 additions & 21 deletions crates/ruborist/src/resolver/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,11 +707,20 @@ pub async fn process_dependency<R: RegistryClient + crate::util::maybe_send::May
/// // Add initial dependency edges to root...
/// build_deps(&mut graph, &registry, PeerDeps::Include).await?;
/// ```
pub async fn build_deps<R: RegistryClient + crate::util::maybe_send::MaybeSync>(
pub async fn build_deps<
R: RegistryClient
+ Clone
+ crate::util::maybe_send::MaybeSend
+ crate::util::maybe_send::MaybeSync
+ 'static,
>(
graph: &mut DependencyGraph,
registry: &R,
peer_deps: PeerDeps,
) -> Result<(), ResolveError<R::Error>> {
) -> Result<(), ResolveError<R::Error>>
where
R::Error: crate::util::maybe_send::MaybeSend,
{
let config = BuildDepsConfig::default().with_peer_deps(peer_deps);
build_deps_with_config(graph, registry, config, &NoopReceiver).await
}
Expand All @@ -730,14 +739,21 @@ pub async fn build_deps<R: RegistryClient + crate::util::maybe_send::MaybeSync>(
/// * `peer_deps` - How to handle peer dependencies
/// * `receiver` - Event receiver for handling build events
pub async fn build_deps_with_receiver<
R: RegistryClient + crate::util::maybe_send::MaybeSync,
R: RegistryClient
+ Clone
+ crate::util::maybe_send::MaybeSend
+ crate::util::maybe_send::MaybeSync
+ 'static,
E: EventReceiver,
>(
graph: &mut DependencyGraph,
registry: &R,
peer_deps: PeerDeps,
receiver: &E,
) -> Result<(), ResolveError<R::Error>> {
) -> Result<(), ResolveError<R::Error>>
where
R::Error: crate::util::maybe_send::MaybeSend,
{
let config = BuildDepsConfig::default().with_peer_deps(peer_deps);
build_deps_with_config(graph, registry, config, receiver).await
}
Expand All @@ -763,14 +779,21 @@ pub async fn build_deps_with_receiver<
/// build_deps_with_config(&mut graph, &registry, config, &receiver).await?;
/// ```
pub async fn build_deps_with_config<
R: RegistryClient + crate::util::maybe_send::MaybeSync,
R: RegistryClient
+ Clone
+ crate::util::maybe_send::MaybeSend
+ crate::util::maybe_send::MaybeSync
+ 'static,
E: EventReceiver,
>(
graph: &mut DependencyGraph,
registry: &R,
config: BuildDepsConfig,
receiver: &E,
) -> Result<(), ResolveError<R::Error>> {
) -> Result<(), ResolveError<R::Error>>
where
R::Error: crate::util::maybe_send::MaybeSend,
{
tracing::debug!(
"Starting dependency tree build, peer_deps: {:?}, concurrency: {}, skip_preload: {}",
config.peer_deps,
Expand All @@ -793,14 +816,20 @@ pub async fn build_deps_with_config<

/// Run the preload phase to warm up the cache with manifests.
async fn run_preload_phase<
R: RegistryClient + crate::util::maybe_send::MaybeSync,
R: RegistryClient
+ Clone
+ crate::util::maybe_send::MaybeSend
+ crate::util::maybe_send::MaybeSync
+ 'static,
E: EventReceiver,
>(
graph: &DependencyGraph,
registry: &R,
config: &BuildDepsConfig,
receiver: &E,
) {
) where
R::Error: crate::util::maybe_send::MaybeSend,
{
if config.skip_preload {
return;
}
Expand All @@ -813,18 +842,21 @@ async fn run_preload_phase<
}

tracing::debug!("Preload phase: {} initial dependencies", initial_deps.len());
receiver.on_event(BuildEvent::PreloadStart {
count: initial_deps.len(),
});
// Note: PreloadStart is emitted inside `preload_manifests` itself now.

let preload_config = PreloadConfig {
peer_deps: config.peer_deps,
concurrency: config.concurrency,
};

// Wrap registry in Arc for the worker pool. Each worker holds an
// Arc<R> so resolution runs on tokio's global executor independently
// of the main task driving result aggregation.
let registry_arc = std::sync::Arc::new(registry.clone());

let stats = preload_manifests(
initial_deps,
registry,
registry_arc,
preload_config,
receiver,
|_name, _manifest| {
Expand All @@ -833,17 +865,18 @@ async fn run_preload_phase<
)
.await;

tracing::debug!(
"Preload phase completed: {} success, {} failed",
let elapsed = start.elapsed();
tracing::info!(
"Preload phase: {:.2?} ({} success, {} failed, {} initial deps)",
elapsed,
stats.success_count,
stats.failed_count
stats.failed_count,
stats.total_processed,
);
receiver.on_event(BuildEvent::PreloadComplete {
success: stats.success_count,
failed: stats.failed_count,
});

tracing::debug!("Preload phase: {:?}", start.elapsed());
}

/// Run the BFS traversal phase to build the dependency tree.
Expand Down Expand Up @@ -970,10 +1003,19 @@ use std::path::Path;
/// let pkg: PackageJson = serde_json::from_str(&pkg_content)?;
/// let lock = resolve(&pkg, &registry).await?;
/// ```
pub async fn resolve<R: RegistryClient + crate::util::maybe_send::MaybeSync>(
pub async fn resolve<
R: RegistryClient
+ Clone
+ crate::util::maybe_send::MaybeSend
+ crate::util::maybe_send::MaybeSync
+ 'static,
>(
pkg: &PackageJson,
registry: &R,
) -> Result<PackageLock, ResolveError<R::Error>> {
) -> Result<PackageLock, ResolveError<R::Error>>
where
R::Error: crate::util::maybe_send::MaybeSend,
{
resolve_with_options(pkg, registry, PeerDeps::Include, &NoopReceiver).await
}

Expand All @@ -985,14 +1027,21 @@ pub async fn resolve<R: RegistryClient + crate::util::maybe_send::MaybeSync>(
/// * `peer_deps` - How to handle peer dependencies
/// * `receiver` - Event receiver for progress tracking
pub async fn resolve_with_options<
R: RegistryClient + crate::util::maybe_send::MaybeSync,
R: RegistryClient
+ Clone
+ crate::util::maybe_send::MaybeSend
+ crate::util::maybe_send::MaybeSync
+ 'static,
E: EventReceiver,
>(
pkg: &PackageJson,
registry: &R,
peer_deps: PeerDeps,
receiver: &E,
) -> Result<PackageLock, ResolveError<R::Error>> {
) -> Result<PackageLock, ResolveError<R::Error>>
where
R::Error: crate::util::maybe_send::MaybeSend,
{
// Create graph with root node
let mut graph = DependencyGraph::from_package_json(PathBuf::from("."), pkg.clone());

Expand Down
Loading
Loading