feat: Improve concurrency

This commit is contained in:
2026-10-04 05:53:14 +02:00
parent 992615c116
commit 1ad7a928ae
3 changed files with 54 additions and 31 deletions
+18 -7
View File
@@ -4,6 +4,7 @@ use std::str::FromStr;
use async_walkdir::WalkDir; use async_walkdir::WalkDir;
use futures::StreamExt; use futures::StreamExt;
use futures::future::join_all;
use futures::stream::FuturesUnordered; use futures::stream::FuturesUnordered;
use minijinja::Environment; use minijinja::Environment;
use optional_struct::{Applicable, optional_struct}; use optional_struct::{Applicable, optional_struct};
@@ -87,7 +88,11 @@ impl Cluster {
let default: OptionalClusterDeserialize = if path.exists() { let default: OptionalClusterDeserialize = if path.exists() {
let content = tokio::fs::read_to_string(path).await.unwrap(); let content = tokio::fs::read_to_string(path).await.unwrap();
serde_yaml::from_str(&content).unwrap()
// The deserialization makes a blocking web request
tokio::task::spawn_blocking(move || serde_yaml::from_str(&content).unwrap())
.await
.unwrap()
} else { } else {
Default::default() Default::default()
}; };
@@ -134,15 +139,13 @@ impl Cluster {
pub async fn get_all() -> Vec<Cluster> { pub async fn get_all() -> Vec<Cluster> {
let env = PathEnvironment::new_patches(); let env = PathEnvironment::new_patches();
WalkDir::new(get_talos_path().join("clusters")) let names: Vec<_> = WalkDir::new(get_talos_path().join("clusters"))
.filter_map(|e| async { e.ok() }) .filter_map(|e| async { e.ok() })
.filter(|e| { .filter(|e| {
let e = e.clone(); let e = e.clone();
async move { e.metadata().await.unwrap().is_file() } async move { e.metadata().await.unwrap().is_file() }
}) })
.filter_map(|e| { .filter_map(|e| async move {
let env = env.clone();
async move {
let mut path = PathBuf::from_str(&e.file_name().to_string_lossy()).unwrap(); let mut path = PathBuf::from_str(&e.file_name().to_string_lossy()).unwrap();
path.set_extension(""); path.set_extension("");
@@ -152,10 +155,18 @@ impl Cluster {
return None; return None;
} }
Some(Cluster::get(&name, &env).await) Some(name)
}
}) })
.collect() .collect()
.await;
// Get the clusters concurrently
join_all(names.iter().map(|name| {
let env = env.clone();
let name = name.clone();
async move { Cluster::get(&name, &env).await }
}))
.await .await
} }
+15 -2
View File
@@ -8,6 +8,7 @@ use crete::cluster::Cluster;
use crete::environment::PathEnvironment; use crete::environment::PathEnvironment;
use crete::{get_configs_path, get_repo_path, set_repo_path}; use crete::{get_configs_path, get_repo_path, set_repo_path};
use futures::StreamExt; use futures::StreamExt;
use futures::future::join_all;
use minijinja::context; use minijinja::context;
use thiserror::Error; use thiserror::Error;
use tokio::process::Command; use tokio::process::Command;
@@ -69,13 +70,25 @@ async fn generate(opts: &GlobalOpts) -> Result<(), Error> {
tokio::fs::create_dir_all(&path).await.unwrap(); tokio::fs::create_dir_all(&path).await.unwrap();
// Generate config files // Generate config files
for cluster in clusters { join_all(clusters.iter().map(|cluster| async move {
for node in cluster.nodes() { join_all(cluster.nodes().iter().map(|node| {
let cluster = cluster.clone();
async move {
run_command(node.talosctl_gen_config_command(&cluster)).await; run_command(node.talosctl_gen_config_command(&cluster)).await;
} }
}))
.await;
// Run these commands in parallel with the the node commands
// NOTE: These do need to happen in the correct order
run_command(cluster.talosctl_gen_config_command()).await; run_command(cluster.talosctl_gen_config_command()).await;
run_command(cluster.talosctl_add_endpoint_command()).await; run_command(cluster.talosctl_add_endpoint_command()).await;
}))
.await;
// Merge all the cluster talosconfig files into a single file
// Since this all modifies the same output file this has to happen sequentially
for cluster in clusters {
run_command(cluster.talosctl_merge_command()).await; run_command(cluster.talosctl_merge_command()).await;
} }
+2 -3
View File
@@ -29,8 +29,8 @@ where
// We can't use async directly inside a custom deserializer, and using the blocking client also // We can't use async directly inside a custom deserializer, and using the blocking client also
// does not work as it will try to start tokio inside of tokio. // does not work as it will try to start tokio inside of tokio.
// So we have do this to work around it. // So we have do this to work around it.
let response = tokio::task::block_in_place(|| { // NOTE: This requires that the deserialization happens inside spawn_blocking
Handle::current().block_on(async { let response = Handle::current().block_on(async {
let res = client let res = client
.post("https://factory.talos.dev/schematics") .post("https://factory.talos.dev/schematics")
.body(content) .body(content)
@@ -43,7 +43,6 @@ where
.map_err(serde::de::Error::custom)?; .map_err(serde::de::Error::custom)?;
Ok(response) Ok(response)
})
})?; })?;
Ok(response.id) Ok(response.id)