Skip to content

Commit

Permalink
futureproof with a PackageResolutionStatistics
Browse files Browse the repository at this point in the history
  • Loading branch information
Eh2406 committed Dec 5, 2024
1 parent 446fadf commit 8255e83
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 21 deletions.
14 changes: 11 additions & 3 deletions examples/caching_dependency_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

use std::cell::RefCell;

use pubgrub::{resolve, Dependencies, DependencyProvider, OfflineDependencyProvider, Ranges};
use pubgrub::{
resolve, Dependencies, DependencyProvider, OfflineDependencyProvider,
PackageResolutionStatistics, Ranges,
};

type NumVS = Ranges<u32>;

Expand Down Expand Up @@ -57,8 +60,13 @@ impl<DP: DependencyProvider<M = String>> DependencyProvider for CachingDependenc

type Priority = DP::Priority;

fn prioritize(&self, package: &DP::P, ranges: &DP::VS, conflict_count: u32) -> Self::Priority {
self.remote_dependencies.prioritize(package, ranges, conflict_count)
fn prioritize(
&self,
package: &DP::P,
ranges: &DP::VS,
statis: &PackageResolutionStatistics,
) -> Self::Priority {
self.remote_dependencies.prioritize(package, ranges, statis)
}

type Err = DP::Err;
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ pub use report::{
DefaultStringReportFormatter, DefaultStringReporter, DerivationTree, Derived, External,
ReportFormatter, Reporter,
};
pub use solver::{resolve, Dependencies, DependencyProvider};
pub use solver::{resolve, Dependencies, DependencyProvider, PackageResolutionStatistics};
pub use term::Term;
pub use type_aliases::{DependencyConstraints, Map, SelectedDependencies, Set};
pub use version::{SemanticVersion, VersionParseError};
Expand Down
14 changes: 11 additions & 3 deletions src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use std::cmp::Reverse;
use std::collections::BTreeMap;
use std::convert::Infallible;

use crate::{Dependencies, DependencyConstraints, DependencyProvider, Map, Package, VersionSet};
use crate::{
Dependencies, DependencyConstraints, DependencyProvider, Map, Package,
PackageResolutionStatistics, VersionSet,
};

/// A basic implementation of [DependencyProvider].
#[derive(Debug, Clone, Default)]
Expand Down Expand Up @@ -95,7 +98,12 @@ impl<P: Package, VS: VersionSet> DependencyProvider for OfflineDependencyProvide
type Priority = (u32, Reverse<u32>);

#[inline]
fn prioritize(&self, package: &P, range: &VS, conflict_count: u32) -> Self::Priority {
fn prioritize(
&self,
package: &P,
range: &VS,
statis: &PackageResolutionStatistics,
) -> Self::Priority {
let version_count = self
.dependencies
.get(package)
Expand All @@ -104,7 +112,7 @@ impl<P: Package, VS: VersionSet> DependencyProvider for OfflineDependencyProvide
if version_count == 0 {
return (u32::MAX, Reverse(0));
}
(conflict_count, Reverse(version_count as u32))
(statis.conflict_count(), Reverse(version_count as u32))
}

#[inline]
Expand Down
53 changes: 45 additions & 8 deletions src/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,8 @@ pub fn resolve<DP: DependencyProvider>(

let Some(highest_priority_pkg) =
state.partial_solution.pick_highest_priority_pkg(|p, r| {
dependency_provider.prioritize(
&state.package_store[p],
r,
state.conflict_count.get(&p).cloned().unwrap_or_default(),
)
let statis = PackageResolutionStatistics::new(p, &state.conflict_count);
dependency_provider.prioritize(&state.package_store[p], r, &statis)
})
else {
return Ok(state
Expand Down Expand Up @@ -203,6 +200,46 @@ pub enum Dependencies<P: Package, VS: VersionSet, M: Eq + Clone + Debug + Displa
Available(DependencyConstraints<P, VS>),
}

/// Some statistics about how much trouble the resolver has had with a package.
pub struct PackageResolutionStatistics {
discovery_order: u32,
conflict_count: u32,
}

impl PackageResolutionStatistics {
fn new<P: Package>(pid: Id<P>, conflict_count: &Map<Id<P>, u32>) -> Self {
Self {
discovery_order: pid.into_raw() as u32,
conflict_count: conflict_count.get(&pid).cloned().unwrap_or_default(),
}
}

/// The number of packages resolution new about the first time this package was mentioned.
///
/// The root package will return `0`. It's direct dependencies will start at `1` and go up from there.
/// Prioritizing based on this value directly will lead to a depth first search of the resolution graph.
/// Prioritizing based on the reverse of this value will lead to a breadth first search of the resolution graph.
///
/// Note: The exact values depend on implementation details of PubGrub and its dependencies.
/// So should not be relied on and may change between any lock file update.
pub fn discovery_order(&self) -> u32 {
self.discovery_order
}

/// The number of times this package was involved in a conflict that caused a back jump.
///
/// When resolution is proceeding normally, this value will stay at `0` for all packages.
/// Therefore, using this for prioritization will not affect the properties of simple cases
/// like checking a lock file.
/// Prioritizing based on this value directly allows the resolver to focus on the packages
/// it is having the most problems with.
///
/// Note: The exact values depend on implementation details of PubGrub. So should not be relied on and may change.
pub fn conflict_count(&self) -> u32 {
self.conflict_count
}
}

/// Trait that allows the algorithm to retrieve available packages and their dependencies.
/// An implementor needs to be supplied to the [resolve] function.
pub trait DependencyProvider {
Expand Down Expand Up @@ -238,8 +275,8 @@ pub trait DependencyProvider {
///
/// Every time such a decision must be made, the resolver looks at all the potential valid
/// packages that have changed, and a asks the dependency provider how important each one is.
/// For each one it calls `prioritize` with the name of the package and the current set of
/// acceptable versions.
/// For each one it calls `prioritize` with the name of the package, the current set of
/// acceptable versions, and some statistics about how much trouble the resolver has had with that package.
/// The resolver will then pick the package with the highes priority from all the potential valid
/// packages.
///
Expand All @@ -262,7 +299,7 @@ pub trait DependencyProvider {
&self,
package: &Self::P,
range: &Self::VS,
conflict_count: u32,
statis: &PackageResolutionStatistics,
) -> Self::Priority;
/// The type returned from `prioritize`. The resolver does not care what type this is
/// as long as it can pick a largest one and clone it.
Expand Down
22 changes: 16 additions & 6 deletions tests/proptest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use proptest::string::string_regex;

use pubgrub::{
resolve, DefaultStringReporter, Dependencies, DependencyProvider, DerivationTree, External,
OfflineDependencyProvider, Package, PubGrubError, Ranges, Reporter, SelectedDependencies,
VersionSet,
OfflineDependencyProvider, Package, PackageResolutionStatistics, PubGrubError, Ranges,
Reporter, SelectedDependencies, VersionSet,
};

use crate::sat_dependency_provider::SatResolve;
Expand Down Expand Up @@ -49,8 +49,13 @@ impl<P: Package, VS: VersionSet> DependencyProvider for OldestVersionsDependency

type Priority = <OfflineDependencyProvider<P, VS> as DependencyProvider>::Priority;

fn prioritize(&self, package: &P, range: &VS, conflict_count: u32) -> Self::Priority {
self.0.prioritize(package, range, conflict_count)
fn prioritize(
&self,
package: &P,
range: &VS,
statis: &PackageResolutionStatistics,
) -> Self::Priority {
self.0.prioritize(package, range, statis)
}

type Err = Infallible;
Expand Down Expand Up @@ -104,8 +109,13 @@ impl<DP: DependencyProvider> DependencyProvider for TimeoutDependencyProvider<DP

type Priority = DP::Priority;

fn prioritize(&self, package: &DP::P, range: &DP::VS, conflict_count: u32) -> Self::Priority {
self.dp.prioritize(package, range, conflict_count)
fn prioritize(
&self,
package: &DP::P,
range: &DP::VS,
statis: &PackageResolutionStatistics,
) -> Self::Priority {
self.dp.prioritize(package, range, statis)
}

type Err = DP::Err;
Expand Down

0 comments on commit 8255e83

Please sign in to comment.