dynamo_memory/nixl/
config.rs1use anyhow::{Result, bail};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13#[derive(Debug, Clone, Default, Serialize, Deserialize)]
39pub struct NixlBackendConfig {
40 #[serde(default)]
46 backends: HashMap<String, HashMap<String, String>>,
47}
48
49impl NixlBackendConfig {
50 pub fn new(backends: HashMap<String, HashMap<String, String>>) -> Self {
54 Self { backends }
55 }
56
57 pub fn from_env() -> Result<Self> {
66 let mut backends = HashMap::new();
67
68 for (key, value) in std::env::vars() {
70 if let Some(remainder) = key.strip_prefix("DYN_KVBM_NIXL_BACKEND_") {
71 if remainder.contains('_') {
73 bail!(
74 "Custom NIXL backend parameters are not yet supported. \
75 Found: {}. Please use only DYN_KVBM_NIXL_BACKEND_<backend>=true \
76 to enable backends with default parameters.",
77 key
78 );
79 }
80
81 let backend_name = remainder.to_uppercase();
83 match crate::parse_bool(&value) {
84 Ok(true) => {
85 backends.insert(backend_name, HashMap::new());
86 }
87 Ok(false) => {
88 continue;
90 }
91 Err(e) => bail!("Invalid value for {}: {}", key, e),
92 }
93 }
94 }
95
96 Ok(Self { backends })
97 }
98
99 pub fn with_backend(mut self, backend: impl Into<String>) -> Self {
102 self.backends
103 .insert(backend.into().to_uppercase(), HashMap::new());
104 self
105 }
106
107 pub fn with_backend_params(
110 mut self,
111 backend: impl Into<String>,
112 params: HashMap<String, String>,
113 ) -> Self {
114 self.backends.insert(backend.into().to_uppercase(), params);
115 self
116 }
117
118 pub fn backends(&self) -> Vec<String> {
120 self.backends.keys().cloned().collect()
121 }
122
123 pub fn backend_params(&self, backend: &str) -> Option<&HashMap<String, String>> {
128 self.backends.get(&backend.to_uppercase())
129 }
130
131 pub fn has_backend(&self, backend: &str) -> bool {
133 self.backends.contains_key(&backend.to_uppercase())
134 }
135
136 pub fn merge(mut self, other: NixlBackendConfig) -> Self {
141 self.backends.extend(other.backends);
142 self
143 }
144
145 pub fn iter(&self) -> impl Iterator<Item = (&String, &HashMap<String, String>)> {
147 self.backends.iter()
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_new_config_is_empty() {
157 let config = NixlBackendConfig::default();
158 assert_eq!(config.backends().len(), 0);
159 }
160
161 #[test]
162 fn test_default_is_empty() {
163 let config = NixlBackendConfig::default();
164 assert!(config.backends().is_empty()); }
166
167 #[test]
168 fn test_with_backend() {
169 let config = NixlBackendConfig::default()
170 .with_backend("ucx")
171 .with_backend("gds_mt");
172
173 assert!(config.has_backend("ucx"));
174 assert!(config.has_backend("UCX"));
175 assert!(config.has_backend("gds_mt"));
176 assert!(config.has_backend("GDS_MT"));
177 assert!(!config.has_backend("other"));
178 }
179
180 #[test]
181 fn test_with_backend_params() {
182 let mut params = HashMap::new();
183 params.insert("threads".to_string(), "4".to_string());
184 params.insert("buffer_size".to_string(), "1048576".to_string());
185
186 let config = NixlBackendConfig::default()
187 .with_backend("UCX")
188 .with_backend_params("GDS", params);
189
190 let ucx_params = config.backend_params("UCX").unwrap();
192 assert!(ucx_params.is_empty());
193
194 let gds_params = config.backend_params("GDS").unwrap();
196 assert_eq!(gds_params.get("threads"), Some(&"4".to_string()));
197 assert_eq!(gds_params.get("buffer_size"), Some(&"1048576".to_string()));
198 }
199
200 #[test]
201 fn test_merge_configs() {
202 let config1 = NixlBackendConfig::default().with_backend("ucx");
203 let config2 = NixlBackendConfig::default().with_backend("gds");
204
205 let merged = config1.merge(config2);
206
207 assert!(merged.has_backend("ucx"));
208 assert!(merged.has_backend("gds"));
209 }
210
211 #[test]
212 fn test_backend_name_case_insensitive() {
213 let config = NixlBackendConfig::default()
214 .with_backend("ucx")
215 .with_backend("Gds_mt")
216 .with_backend("OTHER");
217
218 assert!(config.has_backend("UCX"));
219 assert!(config.has_backend("ucx"));
220 assert!(config.has_backend("GDS_MT"));
221 assert!(config.has_backend("gds_mt"));
222 assert!(config.has_backend("OTHER"));
223 assert!(config.has_backend("other"));
224 }
225
226 #[test]
227 fn test_iter() {
228 let mut params = HashMap::new();
229 params.insert("key".to_string(), "value".to_string());
230
231 let config = NixlBackendConfig::default()
232 .with_backend("UCX")
233 .with_backend_params("GDS", params);
234
235 let items: Vec<_> = config.iter().collect();
236 assert_eq!(items.len(), 2);
237 }
238
239 }