diff --git a/bottlecap/src/proc/mod.rs b/bottlecap/src/proc/mod.rs index 30dc43e5b..53dca9386 100644 --- a/bottlecap/src/proc/mod.rs +++ b/bottlecap/src/proc/mod.rs @@ -5,6 +5,7 @@ use std::{ collections::HashMap, fs::{self, File}, io::{self, BufRead}, + sync::LazyLock, }; use constants::{ @@ -14,6 +15,14 @@ use constants::{ use regex::Regex; use tracing::debug; +// Compiled once on first use rather than on every call. Both patterns capture +// the soft limit value (the first numeric value after the title) from a +// process `limits` file. +static MAX_OPEN_FILES_RE: LazyLock = + LazyLock::new(|| Regex::new(r"^Max open files\s+(\d+)").expect("valid static regex")); +static MAX_PROCESSES_RE: LazyLock = + LazyLock::new(|| Regex::new(r"^Max processes\s+(\d+)").expect("valid static regex")); + #[must_use] pub fn get_pid_list() -> Vec { get_pid_list_from_path(PROC_PATH) @@ -222,8 +231,7 @@ pub fn get_fd_max_data(pids: &[i64]) -> f64 { fn get_fd_max_data_from_path(path: &str, pids: &[i64]) -> f64 { let mut fd_max = constants::LAMBDA_FILE_DESCRIPTORS_DEFAULT_LIMIT; - // regex to capture the soft limit value (first numeric value after the title) - let re = Regex::new(r"^Max open files\s+(\d+)").expect("Failed to create regex"); + let re = &*MAX_OPEN_FILES_RE; for &pid in pids { let limits_path = format!("{path}/{pid}/limits"); @@ -273,8 +281,7 @@ pub fn get_threads_max_data(pids: &[i64]) -> f64 { fn get_threads_max_data_from_path(path: &str, pids: &[i64]) -> f64 { let mut threads_max = constants::LAMBDA_EXECUTION_PROCESSES_DEFAULT_LIMIT; - // regex to capture the soft limit value (first numeric value after the title) - let re = Regex::new(r"^Max processes\s+(\d+)").expect("Failed to create regex"); + let re = &*MAX_PROCESSES_RE; for &pid in pids { let limits_path = format!("{path}/{pid}/limits"); diff --git a/bottlecap/src/tags/lambda/tags.rs b/bottlecap/src/tags/lambda/tags.rs index b80574944..7143736b1 100644 --- a/bottlecap/src/tags/lambda/tags.rs +++ b/bottlecap/src/tags/lambda/tags.rs @@ -65,6 +65,12 @@ const RESOURCE_KEY: &str = "resource"; #[derive(Debug, Clone)] pub struct Lambda { tags_map: HashMap, + // Cached representations computed once at construction so that the hot + // getters below are O(1) reads instead of re-iterating `tags_map` and + // re-running `format!("{k}:{v}")`/`join(",")` on every call. + tags_vec: Vec, + tags_string: String, + function_tags_map: HashMap, } fn arch_to_platform<'a>() -> &'a str { @@ -227,17 +233,29 @@ impl Lambda { config: Arc, metadata: &HashMap, ) -> Self { + let tags_map = tags_from_env(HashMap::new(), config, metadata); + // Compute the derived representations once. The tag set is immutable + // after construction, so callers can read these cached values cheaply. + let tags_vec: Vec = tags_map.iter().map(|(k, v)| format!("{k}:{v}")).collect(); + let tags_string = tags_vec.join(","); + let function_tags_map = + HashMap::from_iter([(FUNCTION_TAGS_KEY.to_string(), tags_string.clone())]); Lambda { - tags_map: tags_from_env(HashMap::new(), config, metadata), + tags_map, + tags_vec, + tags_string, + function_tags_map, } } #[must_use] pub fn get_tags_vec(&self) -> Vec { - self.tags_map - .iter() - .map(|(k, v)| format!("{k}:{v}")) - .collect() + self.tags_vec.clone() + } + + #[must_use] + pub fn get_tags_string(&self) -> &str { + &self.tags_string } #[must_use] @@ -257,13 +275,7 @@ impl Lambda { #[must_use] pub fn get_function_tags_map(&self) -> HashMap { - let tags = self - .tags_map - .iter() - .map(|(k, v)| format!("{k}:{v}")) - .collect::>() - .join(","); - HashMap::from_iter([(FUNCTION_TAGS_KEY.to_string(), tags)]) + self.function_tags_map.clone() } } diff --git a/bottlecap/src/tags/provider.rs b/bottlecap/src/tags/provider.rs index e85609967..dbc2d26e3 100644 --- a/bottlecap/src/tags/provider.rs +++ b/bottlecap/src/tags/provider.rs @@ -39,7 +39,7 @@ impl Provider { #[must_use] pub fn get_tags_string(&self) -> String { - self.get_tags_vec().join(",") + self.tag_provider.get_tags_string().to_string() } #[must_use] @@ -65,6 +65,7 @@ impl Provider { trait GetTags { fn get_tags_vec(&self) -> Vec; + fn get_tags_string(&self) -> &str; fn get_canonical_id(&self) -> Option; fn get_canonical_resource_name(&self) -> Option; fn get_tags_map(&self) -> &hash_map::HashMap; @@ -78,6 +79,12 @@ impl GetTags for TagProvider { } } + fn get_tags_string(&self) -> &str { + match self { + TagProvider::Lambda(lambda_tags) => lambda_tags.get_tags_string(), + } + } + fn get_canonical_id(&self) -> Option { match self { TagProvider::Lambda(lambda_tags) => lambda_tags.get_function_arn().cloned(),