Skip to content

Commit

Permalink
[move-package] Support dependency overrides (#11181)
Browse files Browse the repository at this point in the history
## Description 

When building a dependency graph, different versions of the same
(transitively) dependent package can be encountered. If this is indeed
the case, a single version must be chosen by the developer to be the
override, and this override must be specified in a manifest file whose
package dominates all the conflicting "uses" of the dependent package.
These overrides must taken into consideration during the dependency
graph construction and this PR implements the relevant changes to
dependency graph construction algorithm. For additional details see the
doc-comments in the code as well as the PR this one is based on
(move-language/move#1023) which contains a
discussion of issues encountered during development of this algorithm.

## Test Plan 

A comprehensive test suite is attached.
---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [x] user-visible impact

### Release notes

This change allows a developer to override transitive dependencies of a
package they are developing to avoid conflicts that could otherwise
arise.
  • Loading branch information
awelc authored Apr 21, 2023
1 parent 288186e commit 98be335
Show file tree
Hide file tree
Showing 164 changed files with 4,833 additions and 160 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@ pub fn parse_dependency(dep_name: &str, mut tval: TV) -> Result<PM::Dependency>
.transpose()?;
let version = table.remove("version").map(parse_version).transpose()?;
let digest = table.remove("digest").map(parse_digest).transpose()?;
let dep_override = table
.remove("override")
.map(parse_dep_override)
.transpose()?
.map_or(false, |o| o);

let kind = match (
table.remove("local"),
Expand All @@ -350,7 +355,10 @@ pub fn parse_dependency(dep_name: &str, mut tval: TV) -> Result<PM::Dependency>
bail!("Local source path not a string")
};

PM::DependencyKind::Local(local)
PM::DependencyKind::Local(
// with allow_cwd_parent set to true, it never fails
PM::normalize_path(local, true /* allow_cwd_parent */).unwrap(),
)
}

(None, subdir, Some(git_url), None) => {
Expand Down Expand Up @@ -433,6 +441,7 @@ pub fn parse_dependency(dep_name: &str, mut tval: TV) -> Result<PM::Dependency>
subst,
version,
digest,
dep_override,
}))
}

Expand Down Expand Up @@ -502,6 +511,13 @@ fn parse_digest(tval: TV) -> Result<PM::PackageDigest> {
Ok(PM::PackageDigest::from(digest_str))
}

fn parse_dep_override(tval: TV) -> Result<PM::DepOverride> {
if !tval.is_bool() {
bail!("Invalid dependency override value");
}
Ok(tval.as_bool().unwrap())
}

// check that only recognized names are provided at the top-level
fn warn_if_unknown_field_names(table: &toml::map::Map<String, TV>, known_names: &[&str]) {
let mut unknown_names = BTreeSet::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub type NamedAddress = Symbol;
pub type PackageName = Symbol;
pub type FileName = Symbol;
pub type PackageDigest = Symbol;
pub type DepOverride = bool;

pub type AddressDeclarations = BTreeMap<NamedAddress, Option<AccountAddress>>;
pub type DevAddressDeclarations = BTreeMap<NamedAddress, AccountAddress>;
Expand Down Expand Up @@ -55,6 +56,7 @@ pub struct InternalDependency {
pub subst: Option<Substitution>,
pub version: Option<Version>,
pub digest: Option<PackageDigest>,
pub dep_override: DepOverride,
}

#[derive(Debug, Clone, Eq, PartialEq)]
Expand Down Expand Up @@ -154,7 +156,7 @@ impl Default for DependencyKind {
/// or is prefixed by accesses to parent directories when `allow_cwd_parent` is false.
///
/// Returns the normalized path on success.
fn normalize_path(path: impl AsRef<Path>, allow_cwd_parent: bool) -> Result<PathBuf> {
pub fn normalize_path(path: impl AsRef<Path>, allow_cwd_parent: bool) -> Result<PathBuf> {
use Component::*;

let mut stack = Vec::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use std::{
collections::BTreeSet,
collections::{BTreeMap, BTreeSet},
fs::{self, File},
io::Write,
path::PathBuf,
Expand Down Expand Up @@ -184,7 +184,15 @@ fn merge_simple() {
)
.expect("Reading inner");

assert!(outer.merge(inner, Symbol::from("")).is_ok());
assert!(outer
.merge(
Symbol::from("A"),
Symbol::from("A"),
inner,
Symbol::from(""),
&BTreeMap::new(),
)
.is_ok());

assert_eq!(
outer.topological_order(),
Expand Down Expand Up @@ -214,7 +222,15 @@ fn merge_into_root() {
)
.expect("Reading inner");

assert!(outer.merge(inner, Symbol::from("")).is_ok());
assert!(outer
.merge(
Symbol::from("Root"),
Symbol::from("A"),
inner,
Symbol::from(""),
&BTreeMap::new(),
)
.is_ok());

assert_eq!(
outer.topological_order(),
Expand Down Expand Up @@ -243,7 +259,7 @@ fn merge_detached() {
)
.expect("Reading inner");

let Err(err) = outer.merge(inner, Symbol::from("")) else {
let Err(err) = outer.merge(Symbol::from("OtherDep"), Symbol::from("A"), inner, Symbol::from(""), &BTreeMap::new()) else {
panic!("Inner's root is not part of outer's graph, so this should fail");
};

Expand All @@ -267,7 +283,7 @@ fn merge_after_calculating_always_deps() {
)
.expect("Reading inner");

let Err(err) = outer.merge(inner, Symbol::from("")) else {
let Err(err) = outer.merge(Symbol::from("A"),Symbol::from("A"), inner, Symbol::from(""), &BTreeMap::new()) else {
panic!("Outer's always deps have already been calculated so this should fail");
};

Expand Down Expand Up @@ -295,7 +311,15 @@ fn merge_overlapping() {
)
.expect("Reading inner");

assert!(outer.merge(inner, Symbol::from("")).is_ok());
assert!(outer
.merge(
Symbol::from("B"),
Symbol::from("A"),
inner,
Symbol::from(""),
&BTreeMap::new(),
)
.is_ok());
}

#[test]
Expand All @@ -319,7 +343,7 @@ fn merge_overlapping_different_deps() {
)
.expect("Reading inner");

let Err(err) = outer.merge(inner, Symbol::from("")) else {
let Err(err) = outer.merge(Symbol::from("B"),Symbol::from("A"), inner, Symbol::from(""), &BTreeMap::new()) else {
panic!("Outer and inner mention package A which has different dependencies in both.");
};

Expand Down Expand Up @@ -347,7 +371,7 @@ fn merge_cyclic() {
)
.expect("Reading inner");

let Err(err) = outer.merge(inner, Symbol::from("")) else {
let Err(err) = outer.merge(Symbol::from("B"), Symbol::from("Root"), inner, Symbol::from(""), &BTreeMap::new()) else {
panic!("Inner refers back to outer's root");
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,31 @@ ResolvedGraph {
),
version: None,
resolver: None,
overridden_path: false,
},
"B": Package {
kind: Local(
"deps_only/B",
),
version: None,
resolver: None,
overridden_path: false,
},
"C": Package {
kind: Local(
"deps_only/C",
),
version: None,
resolver: None,
overridden_path: false,
},
"D": Package {
kind: Local(
"deps_only/D",
),
version: None,
resolver: None,
overridden_path: false,
},
},
always_deps: {
Expand Down Expand Up @@ -142,6 +146,7 @@ ResolvedGraph {
subst: None,
version: None,
digest: None,
dep_override: false,
},
),
},
Expand All @@ -154,6 +159,7 @@ ResolvedGraph {
subst: None,
version: None,
digest: None,
dep_override: false,
},
),
},
Expand Down Expand Up @@ -189,6 +195,7 @@ ResolvedGraph {
subst: None,
version: None,
digest: None,
dep_override: false,
},
),
},
Expand Down Expand Up @@ -266,33 +273,36 @@ ResolvedGraph {
"A": Internal(
InternalDependency {
kind: Local(
"./deps_only/A",
"deps_only/A",
),
subst: None,
version: None,
digest: None,
dep_override: false,
},
),
"C": Internal(
InternalDependency {
kind: Local(
"./deps_only/C",
"deps_only/C",
),
subst: None,
version: None,
digest: None,
dep_override: false,
},
),
},
dev_dependencies: {
"B": Internal(
InternalDependency {
kind: Local(
"./deps_only/B",
"deps_only/B",
),
subst: None,
version: None,
digest: None,
dep_override: false,
},
),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ResolvedGraph {
),
version: None,
resolver: None,
overridden_path: false,
},
},
always_deps: {
Expand Down Expand Up @@ -104,7 +105,7 @@ ResolvedGraph {
"OtherDep": Internal(
InternalDependency {
kind: Local(
"./deps_only/other_dep",
"deps_only/other_dep",
),
subst: Some(
{
Expand All @@ -117,6 +118,7 @@ ResolvedGraph {
digest: Some(
"6A88B7888D6049EB0121900E22B6FA2C0E702F042C8C8D4FD62AD5C990B9F9A8",
),
dep_override: false,
},
),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,23 @@ ResolvedGraph {
),
version: None,
resolver: None,
overridden_path: false,
},
"B": Package {
kind: Local(
"deps_only/B",
),
version: None,
resolver: None,
overridden_path: false,
},
"C": Package {
kind: Local(
"deps_only/C",
),
version: None,
resolver: None,
overridden_path: false,
},
},
always_deps: {
Expand Down Expand Up @@ -123,6 +126,7 @@ ResolvedGraph {
),
version: None,
digest: None,
dep_override: false,
},
),
},
Expand Down Expand Up @@ -171,6 +175,7 @@ ResolvedGraph {
),
version: None,
digest: None,
dep_override: false,
},
),
},
Expand Down Expand Up @@ -238,17 +243,18 @@ ResolvedGraph {
"A": Internal(
InternalDependency {
kind: Local(
"./deps_only/A",
"deps_only/A",
),
subst: None,
version: None,
digest: None,
dep_override: false,
},
),
"B": Internal(
InternalDependency {
kind: Local(
"./deps_only/B",
"deps_only/B",
),
subst: Some(
{
Expand All @@ -259,6 +265,7 @@ ResolvedGraph {
),
version: None,
digest: None,
dep_override: false,
},
),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Failed to resolve dependencies for package 'Root': Resolving dependencies for package 'B': Conflicting dependencies found:
C = { local = "deps_only/C", version = "2.0.0" }
C = { local = "deps_only/C", version = "1.0.0" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "Root"
version = "0.0.0"

[dependencies]
A = { local = "./deps_only/A" }
B = { local = "./deps_only/B" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "A"
version = "0.0.0"

[dependencies]
C = { local = "../C", version = "2.0.0" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "B"
version = "0.0.0"

[dependencies]
C = { local = "../C", version = "1.0.0" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[package]
name = "C"
version = "0.0.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Failed to resolve dependencies for package 'Root': Adding dependencies from ../resolvers/successful.sh for dependency 'A' in 'Root': Conflicting dependencies found:
ADep = { local = "deps_only/ADep", version = "1.0.0" }
ADep = { local = "deps_only/ADep" } # Resolved by ../resolvers/successful.sh
Loading

1 comment on commit 98be335

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

4 Validators 500/s Owned Transactions Benchmark Results

Benchmark Report:
+-------------+------+------+--------+---------------+---------------+---------------+-----------------------+----------------------------+
| duration(s) | tps  | cps  | error% | latency (min) | latency (p50) | latency (p99) | gas used (MIST total) | gas used/hr (MIST approx.) |
+=========================================================================================================================================+
| 60          | 1000 | 1000 | 0      | 15            | 24            | 37            | 803,186,688,000       | 48,191,201,280,000         |
Stress Performance Report:
+-----------+-----+-----+
| metric    | p50 | p99 |
+=======================+
| cpu usage | 37  | 55  |

4 Validators 500/s Shared Transactions Benchmark Results

Benchmark Report:
+-------------+-----+-----+--------+---------------+---------------+---------------+-----------------------+----------------------------+
| duration(s) | tps | cps | error% | latency (min) | latency (p50) | latency (p99) | gas used (MIST total) | gas used/hr (MIST approx.) |
+=======================================================================================================================================+
| 60          | 995 | 995 | 0      | 14            | 257           | 429           | 961,012,768,800       | 57,660,766,128,000         |
Stress Performance Report:
+-----------+-----+-----+
| metric    | p50 | p99 |
+=======================+
| cpu usage | 40  | 51  |

20 Validators 50/s Owned Transactions Benchmark Results

Benchmark Report:
+-------------+-----+-----+--------+---------------+---------------+---------------+-----------------------+----------------------------+
| duration(s) | tps | cps | error% | latency (min) | latency (p50) | latency (p99) | gas used (MIST total) | gas used/hr (MIST approx.) |
+=======================================================================================================================================+
| 60          | 200 | 200 | 0      | 29            | 74            | 95            | 160,751,616,000       | 9,645,096,960,000          |
Stress Performance Report:
+-----------+-----+-----+
| metric    | p50 | p99 |
+=======================+
| cpu usage | 46  | 62  |

20 Validators 50/s Shared Transactions Benchmark Results

Benchmark Report:
+-------------+-----+-----+--------+---------------+---------------+---------------+-----------------------+----------------------------+
| duration(s) | tps | cps | error% | latency (min) | latency (p50) | latency (p99) | gas used (MIST total) | gas used/hr (MIST approx.) |
+=======================================================================================================================================+
| 60          | 197 | 197 | 0      | 66            | 535           | 968           | 190,489,666,800       | 11,429,380,008,000         |
Stress Performance Report:
+-----------+-----+-----+
| metric    | p50 | p99 |
+=======================+
| cpu usage | 48  | 66  |

Narwhal Benchmark Results

 SUMMARY:
-----------------------------------------
 + CONFIG:
 Faults: 0 node(s)
 Committee size: 4 node(s)
 Worker(s) per node: 1 worker(s)
 Collocate primary and workers: True
 Input rate: 50,000 tx/s
 Transaction size: 512 B
 Execution time: 0 s

 Header number of batches threshold: 32 digests
 Header maximum number of batches: 1,000 digests
 Max header delay: 2,000 ms
 GC depth: 50 round(s)
 Sync retry delay: 10,000 ms
 Sync retry nodes: 3 node(s)
 batch size: 500,000 B
 Max batch delay: 200 ms
 Max concurrent requests: 500,000 

 + RESULTS:
 Batch creation avg latency: 202 ms
 Header creation avg latency: -1 ms
 	Batch to header avg latency: -1 ms
 Header to certificate avg latency: 2 ms
 	Request vote outbound avg latency: 0 ms
 Certificate commit avg latency: 851 ms

 Consensus TPS: 0 tx/s
 Consensus BPS: 0 B/s
 Consensus latency: 0 ms

 End-to-end TPS: 0 tx/s
 End-to-end BPS: 0 B/s
 End-to-end latency: 0 ms
-----------------------------------------

Please sign in to comment.