From 7e9489a65aba3e6993d8b96f6ef09e34a50837c1 Mon Sep 17 00:00:00 2001 From: Adam Spofford <93943719+adamspofford-dfinity@users.noreply.github.com> Date: Thu, 25 Jul 2024 09:53:41 -0700 Subject: [PATCH 1/5] Release 0.37.1 (#576) --- CHANGELOG.md | 2 ++ Cargo.lock | 12 ++++++------ Cargo.toml | 8 ++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f95901ec..09e8a970 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [0.37.1] - 2024-07-25 + * Bug fix: Add `api/v2` prefix to read_state requests for hyper transport ## [0.37.0] - 2024-07-23 diff --git a/Cargo.lock b/Cargo.lock index 7a31d329..c2e4fb46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1083,7 +1083,7 @@ dependencies = [ [[package]] name = "ic-agent" -version = "0.37.0" +version = "0.37.1" dependencies = [ "async-lock", "backoff", @@ -1148,7 +1148,7 @@ dependencies = [ [[package]] name = "ic-identity-hsm" -version = "0.37.0" +version = "0.37.1" dependencies = [ "hex", "ic-agent", @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "ic-transport-types" -version = "0.37.0" +version = "0.37.1" dependencies = [ "candid", "hex", @@ -1176,7 +1176,7 @@ dependencies = [ [[package]] name = "ic-utils" -version = "0.37.0" +version = "0.37.1" dependencies = [ "async-trait", "candid", @@ -1239,7 +1239,7 @@ dependencies = [ [[package]] name = "icx" -version = "0.37.0" +version = "0.37.1" dependencies = [ "anyhow", "candid", @@ -1257,7 +1257,7 @@ dependencies = [ [[package]] name = "icx-cert" -version = "0.37.0" +version = "0.37.1" dependencies = [ "anyhow", "base64", diff --git a/Cargo.toml b/Cargo.toml index a435ed5e..535c20fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ ] [workspace.package] -version = "0.37.0" +version = "0.37.1" authors = ["DFINITY Stiftung "] edition = "2021" repository = "https://github.com/dfinity/agent-rs" @@ -22,9 +22,9 @@ rust-version = "1.75.0" license = "Apache-2.0" [workspace.dependencies] -ic-agent = { path = "ic-agent", version = "0.37.0", default-features = false } -ic-utils = { path = "ic-utils", version = "0.37.0" } -ic-transport-types = { path = "ic-transport-types", version = "0.37.0" } +ic-agent = { path = "ic-agent", version = "0.37.1", default-features = false } +ic-utils = { path = "ic-utils", version = "0.37.1" } +ic-transport-types = { path = "ic-transport-types", version = "0.37.1" } ic-certification = "2.2" candid = "0.10.1" From 94640f0d7ba3a52452b87fc7a76f1ff48d6f1cce Mon Sep 17 00:00:00 2001 From: Nikolay Komarevskiy Date: Fri, 9 Aug 2024 11:34:59 +0200 Subject: [PATCH 2/5] feat: add route provider benches --- ic-agent/Cargo.toml | 7 + ic-agent/benches/perf_route_provider.rs | 132 ++++++++++++++++++ .../http_transport/dynamic_routing/mod.rs | 5 +- .../dynamic_routing/test_utils.rs | 23 +-- 4 files changed, 156 insertions(+), 11 deletions(-) create mode 100644 ic-agent/benches/perf_route_provider.rs diff --git a/ic-agent/Cargo.toml b/ic-agent/Cargo.toml index 6be9470f..4f5b1f36 100644 --- a/ic-agent/Cargo.toml +++ b/ic-agent/Cargo.toml @@ -93,6 +93,7 @@ web-sys = { version = "0.3", features = ["Window"], optional = true } [dev-dependencies] serde_json.workspace = true +criterion = "0.5" [target.'cfg(not(target_family = "wasm"))'.dev-dependencies] tokio = { workspace = true, features = ["full"] } @@ -133,8 +134,14 @@ wasm-bindgen = [ "backoff/wasm-bindgen", "cached/wasm", ] +bench = [] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] rustdoc-args = ["--cfg=docsrs"] features = ["hyper"] + +[[bench]] +name = "perf_route_provider" +path = "benches/perf_route_provider.rs" +harness = false \ No newline at end of file diff --git a/ic-agent/benches/perf_route_provider.rs b/ic-agent/benches/perf_route_provider.rs new file mode 100644 index 00000000..b7b9d85b --- /dev/null +++ b/ic-agent/benches/perf_route_provider.rs @@ -0,0 +1,132 @@ +use std::{sync::Arc, time::Duration}; + +use criterion::{criterion_group, criterion_main, Criterion}; +use ic_agent::agent::http_transport::{ + dynamic_routing::{ + dynamic_route_provider::DynamicRouteProviderBuilder, + node::Node, + snapshot::{ + latency_based_routing::LatencyRoutingSnapshot, + round_robin_routing::RoundRobinRoutingSnapshot, routing_snapshot::RoutingSnapshot, + }, + test_utils::{NodeHealthCheckerMock, NodesFetcherMock}, + }, + route_provider::{RoundRobinRouteProvider, RouteProvider}, +}; +use reqwest::Client; +use tokio::{runtime::Handle, sync::oneshot, time::sleep}; + +// To run the benchmark use the command: +// $ cargo bench --bench perf_route_provider --features bench + +// Benchmarking function +fn benchmark_route_providers(c: &mut Criterion) { + // For displaying trace messages of the inner running tasks in dynamic route providers, enable the subscriber below + + // use tracing::Level; + // use tracing_subscriber::FmtSubscriber; + // FmtSubscriber::builder().with_max_level(Level::TRACE).init(); + + // Number of different domains for each route provider + let nodes_count = 100; + + let mut group = c.benchmark_group("RouteProviders"); + group.sample_size(10000); + + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("failed to create runtime"); + + // Setup all route providers + let route_providers = setup_route_providers(nodes_count, runtime.handle().clone()); + + for (name, instance) in route_providers { + group.bench_function(name, |b| { + b.iter(|| { + let _url = instance.route().unwrap(); + }) + }); + } + group.finish(); +} + +criterion_group!(benches, benchmark_route_providers); +criterion_main!(benches); + +fn setup_static_route_provider(nodes_count: usize) -> Arc { + let urls: Vec<_> = (0..nodes_count) + .map(|idx| format!("https://domain_{idx}.app")) + .collect(); + Arc::new(RoundRobinRouteProvider::new(urls).unwrap()) +} + +async fn setup_dynamic_route_provider( + nodes_count: usize, + snapshot: S, +) -> Arc { + let client = Client::builder().build().expect("failed to build a client"); + + let nodes: Vec<_> = (0..nodes_count) + .map(|idx| Node::new(&format!("https://domain_{idx}.app")).unwrap()) + .collect(); + + let fetcher = Arc::new(NodesFetcherMock::new()); + let checker = Arc::new(NodeHealthCheckerMock::new()); + let fetch_interval = Duration::from_secs(1); + let check_interval = Duration::from_secs(1); + + fetcher.overwrite_nodes(nodes.clone()); + checker.overwrite_healthy_nodes(nodes.clone()); + + // Use e.g. a couple of nodes as seeds. + let seeds = nodes[..2].to_vec(); + + let route_provider = DynamicRouteProviderBuilder::new(snapshot, seeds, client.clone()) + .with_fetch_period(fetch_interval) + .with_fetcher(fetcher) + .with_check_period(check_interval) + .with_checker(checker) + .build() + .await; + + Arc::new(route_provider) +} + +fn setup_route_providers( + nodes_count: usize, + runtime: Handle, +) -> Vec<(String, Arc)> { + // Assemble all instances for benching. + let mut route_providers = vec![]; + // Setup static round-robin route provider + route_providers.push(( + "Static round-robin RouteProvider".to_string(), + setup_static_route_provider(nodes_count), + )); + // Setup dynamic round-robin route provider + let (tx, rx) = oneshot::channel(); + runtime.spawn(async move { + let rp = setup_dynamic_route_provider(nodes_count, RoundRobinRoutingSnapshot::new()).await; + tx.send(rp).unwrap(); + sleep(Duration::from_secs(100000)).await; + }); + let route_provider = runtime.block_on(async { rx.await.unwrap() }); + route_providers.push(( + "Dynamic round-robin RouteProvider".to_string(), + route_provider, + )); + // Setup dynamic latency-based route provider + let (tx, rx) = oneshot::channel(); + runtime.spawn(async move { + let rp = setup_dynamic_route_provider(nodes_count, LatencyRoutingSnapshot::new()).await; + tx.send(rp).unwrap(); + sleep(Duration::from_secs(100000)).await; + }); + let route_provider = runtime.block_on(async { rx.await.unwrap() }); + route_providers.push(( + "Dynamic latency-based RouteProvider".to_string(), + route_provider, + )); + route_providers +} diff --git a/ic-agent/src/agent/http_transport/dynamic_routing/mod.rs b/ic-agent/src/agent/http_transport/dynamic_routing/mod.rs index 07570f0f..f163a00e 100644 --- a/ic-agent/src/agent/http_transport/dynamic_routing/mod.rs +++ b/ic-agent/src/agent/http_transport/dynamic_routing/mod.rs @@ -10,7 +10,8 @@ pub mod node; pub mod nodes_fetch; /// Routing snapshot implementation. pub mod snapshot; -#[cfg(test)] -pub(super) mod test_utils; +/// Testing and benchmarking helpers. +#[cfg(any(test, feature = "bench"))] +pub mod test_utils; /// Type aliases used in dynamic routing. pub(super) mod type_aliases; diff --git a/ic-agent/src/agent/http_transport/dynamic_routing/test_utils.rs b/ic-agent/src/agent/http_transport/dynamic_routing/test_utils.rs index 60004d75..f20bce52 100644 --- a/ic-agent/src/agent/http_transport/dynamic_routing/test_utils.rs +++ b/ic-agent/src/agent/http_transport/dynamic_routing/test_utils.rs @@ -17,17 +17,16 @@ use crate::agent::http_transport::{ route_provider::RouteProvider, }; -pub(super) fn route_n_times(n: usize, f: Arc) -> Vec { +/// +pub fn route_n_times(n: usize, f: Arc) -> Vec { (0..n) .map(|_| f.route().unwrap().domain().unwrap().to_string()) .collect() } -pub(super) fn assert_routed_domains( - actual: Vec, - expected: Vec, - expected_repetitions: usize, -) where +/// +pub fn assert_routed_domains(actual: Vec, expected: Vec, expected_repetitions: usize) +where T: AsRef + Eq + Hash + Debug + Ord, { fn build_count_map(items: &[T]) -> HashMap<&T, usize> @@ -56,9 +55,10 @@ pub(super) fn assert_routed_domains( .all(|&x| x == &expected_repetitions)); } +/// A mock implementation of the nodes Fetch trait, without http calls. #[derive(Debug)] -pub(super) struct NodesFetcherMock { - // A set of nodes, existing in the topology. +pub struct NodesFetcherMock { + /// A set of nodes, existing in the topology. pub nodes: AtomicSwap>, } @@ -77,19 +77,22 @@ impl Default for NodesFetcherMock { } impl NodesFetcherMock { + /// Create a new instance. pub fn new() -> Self { Self { nodes: Arc::new(ArcSwap::from_pointee(vec![])), } } + /// Sets the existing nodes in the topology. pub fn overwrite_nodes(&self, nodes: Vec) { self.nodes.store(Arc::new(nodes)); } } +/// A mock implementation of the node's HealthCheck trait, without http calls. #[derive(Debug)] -pub(super) struct NodeHealthCheckerMock { +pub struct NodeHealthCheckerMock { healthy_nodes: Arc>>, } @@ -112,12 +115,14 @@ impl HealthCheck for NodeHealthCheckerMock { } impl NodeHealthCheckerMock { + /// Creates a new instance pub fn new() -> Self { Self { healthy_nodes: Arc::new(ArcSwap::from_pointee(HashSet::new())), } } + /// Sets healthy nodes in the topology. pub fn overwrite_healthy_nodes(&self, healthy_nodes: Vec) { self.healthy_nodes .store(Arc::new(HashSet::from_iter(healthy_nodes))); From d1a364b4b89de85c9e4aa9cf2b48a3e2c49daa99 Mon Sep 17 00:00:00 2001 From: Nikolay Komarevskiy Date: Fri, 9 Aug 2024 13:02:54 +0200 Subject: [PATCH 3/5] chore: use cancellation token --- ic-agent/benches/perf_route_provider.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ic-agent/benches/perf_route_provider.rs b/ic-agent/benches/perf_route_provider.rs index b7b9d85b..a6e6e856 100644 --- a/ic-agent/benches/perf_route_provider.rs +++ b/ic-agent/benches/perf_route_provider.rs @@ -14,7 +14,8 @@ use ic_agent::agent::http_transport::{ route_provider::{RoundRobinRouteProvider, RouteProvider}, }; use reqwest::Client; -use tokio::{runtime::Handle, sync::oneshot, time::sleep}; +use tokio::{runtime::Handle, sync::oneshot}; +use tokio_util::sync::CancellationToken; // To run the benchmark use the command: // $ cargo bench --bench perf_route_provider --features bench @@ -39,7 +40,9 @@ fn benchmark_route_providers(c: &mut Criterion) { .expect("failed to create runtime"); // Setup all route providers - let route_providers = setup_route_providers(nodes_count, runtime.handle().clone()); + let token = CancellationToken::new(); + let route_providers = + setup_route_providers(nodes_count, runtime.handle().clone(), token.clone()); for (name, instance) in route_providers { group.bench_function(name, |b| { @@ -48,6 +51,7 @@ fn benchmark_route_providers(c: &mut Criterion) { }) }); } + token.cancel(); group.finish(); } @@ -96,6 +100,7 @@ async fn setup_dynamic_route_provider( fn setup_route_providers( nodes_count: usize, runtime: Handle, + cancellation_token: CancellationToken, ) -> Vec<(String, Arc)> { // Assemble all instances for benching. let mut route_providers = vec![]; @@ -106,10 +111,11 @@ fn setup_route_providers( )); // Setup dynamic round-robin route provider let (tx, rx) = oneshot::channel(); + let token_cloned = cancellation_token.clone(); runtime.spawn(async move { let rp = setup_dynamic_route_provider(nodes_count, RoundRobinRoutingSnapshot::new()).await; tx.send(rp).unwrap(); - sleep(Duration::from_secs(100000)).await; + token_cloned.cancelled().await; }); let route_provider = runtime.block_on(async { rx.await.unwrap() }); route_providers.push(( @@ -118,10 +124,11 @@ fn setup_route_providers( )); // Setup dynamic latency-based route provider let (tx, rx) = oneshot::channel(); + let token_cloned = cancellation_token.clone(); runtime.spawn(async move { let rp = setup_dynamic_route_provider(nodes_count, LatencyRoutingSnapshot::new()).await; tx.send(rp).unwrap(); - sleep(Duration::from_secs(100000)).await; + token_cloned.cancelled().await; }); let route_provider = runtime.block_on(async { rx.await.unwrap() }); route_providers.push(( From d196b6dff486d0e14c5fe46c9b50680dd8978d17 Mon Sep 17 00:00:00 2001 From: Nikolay Komarevskiy Date: Fri, 9 Aug 2024 13:09:21 +0200 Subject: [PATCH 4/5] chore: change check_interval --- ic-agent/benches/perf_route_provider.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ic-agent/benches/perf_route_provider.rs b/ic-agent/benches/perf_route_provider.rs index a6e6e856..380cb09e 100644 --- a/ic-agent/benches/perf_route_provider.rs +++ b/ic-agent/benches/perf_route_provider.rs @@ -78,7 +78,7 @@ async fn setup_dynamic_route_provider( let fetcher = Arc::new(NodesFetcherMock::new()); let checker = Arc::new(NodeHealthCheckerMock::new()); let fetch_interval = Duration::from_secs(1); - let check_interval = Duration::from_secs(1); + let check_interval = Duration::from_millis(50); fetcher.overwrite_nodes(nodes.clone()); checker.overwrite_healthy_nodes(nodes.clone()); From 1586b5605dea3bb544017e3547e79a33751a02de Mon Sep 17 00:00:00 2001 From: Nikolay Komarevskiy Date: Fri, 9 Aug 2024 13:33:54 +0200 Subject: [PATCH 5/5] chore: add required features --- ic-agent/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ic-agent/Cargo.toml b/ic-agent/Cargo.toml index 4f5b1f36..ce5bda25 100644 --- a/ic-agent/Cargo.toml +++ b/ic-agent/Cargo.toml @@ -93,11 +93,11 @@ web-sys = { version = "0.3", features = ["Window"], optional = true } [dev-dependencies] serde_json.workspace = true -criterion = "0.5" [target.'cfg(not(target_family = "wasm"))'.dev-dependencies] tokio = { workspace = true, features = ["full"] } mockito = "1.0.2" +criterion = "0.5" [target.'cfg(target_family = "wasm")'.dev-dependencies] wasm-bindgen-test = "0.3.34" @@ -144,4 +144,5 @@ features = ["hyper"] [[bench]] name = "perf_route_provider" path = "benches/perf_route_provider.rs" +required-features = ["bench"] harness = false \ No newline at end of file