diff --git a/Cargo.toml b/Cargo.toml index 634e79d..61da317 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,3 +39,7 @@ harness = false [[bench]] name = "bench_inspect" harness = false + +[[bench]] +name = "bench_apply" +harness = false diff --git a/benches/bench_apply.rs b/benches/bench_apply.rs new file mode 100644 index 0000000..78dc788 --- /dev/null +++ b/benches/bench_apply.rs @@ -0,0 +1,59 @@ +use kcd::apply; +use kcd::client::KeycloakClient; +use std::collections::HashMap; +use tokio::runtime::Runtime; + +#[path = "../tests/common/mod.rs"] +mod common; + +fn main() { + let rt = Runtime::new().unwrap(); + rt.block_on(async { + let server_url = common::start_mock_server().await; + let mut client = KeycloakClient::new(server_url); + client.set_target_realm("test-realm".to_string()); + client + .login("admin-cli", None, Some("admin"), Some("admin")) + .await + .unwrap(); + + let dir = tempfile::tempdir().unwrap(); + let workspace_dir = dir.path().to_path_buf(); + let mut realms = Vec::new(); + for i in 0..100 { + let realm_name = format!("realm-{}", i); + let realm_dir = workspace_dir.join(&realm_name); + std::fs::create_dir_all(&realm_dir).unwrap(); + + let realm = kcd::models::RealmRepresentation { + realm: realm_name.clone(), + enabled: Some(true), + display_name: Some(format!("Updated Realm {}", i)), + extra: HashMap::new(), + }; + std::fs::write( + realm_dir.join("realm.yaml"), + serde_yaml::to_string(&realm).unwrap(), + ) + .unwrap(); + + realms.push(realm_name); + } + + let mut total_time = std::time::Duration::new(0, 0); + let iters = 10; + + for _ in 0..iters { + let start = std::time::Instant::now(); + apply::run(&client, workspace_dir.clone(), &realms, true) + .await + .unwrap(); + total_time += start.elapsed(); + } + + println!( + "Average elapsed time (100 realms, 10 iterations): {:?}", + total_time / iters + ); + }); +} diff --git a/src/apply/mod.rs b/src/apply/mod.rs index 1cc85e4..4903594 100644 --- a/src/apply/mod.rs +++ b/src/apply/mod.rs @@ -98,25 +98,37 @@ pub async fn run( return Ok(()); } + let mut set = tokio::task::JoinSet::new(); + for realm_name in realms { - println!( - "\n{} {}", - ACTION, - style(format!("Applying realm: {}", realm_name)) - .cyan() - .bold() - ); let mut realm_client = client.clone(); realm_client.set_target_realm(realm_name.clone()); let realm_dir = workspace_dir.join(&realm_name); - apply_single_realm( - &realm_client, - realm_dir, - Arc::clone(&env_vars), - Arc::clone(&planned_files), - &realm_name, - ) - .await?; + let env_vars = Arc::clone(&env_vars); + let planned_files = Arc::clone(&planned_files); + + set.spawn(async move { + println!( + "\n{} {}", + ACTION, + style(format!("Applying realm: {}", realm_name)) + .cyan() + .bold() + ); + + apply_single_realm( + &realm_client, + realm_dir, + env_vars, + planned_files, + &realm_name, + ) + .await + }); + } + + while let Some(res) = set.join_next().await { + res??; } // Success - remove plan