Skip to main content

nodedb_lite/config/
env.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! [`LiteConfig::from_env`] — environment-variable overrides.
4//!
5//! Every variable is optional and independently applied: an absent or
6//! malformed value leaves the corresponding default in place and logs a
7//! warning rather than failing the open.
8
9use super::types::LiteConfig;
10
11impl LiteConfig {
12    /// Load configuration from environment variables, falling back to defaults
13    /// for any variable that is absent or malformed.
14    ///
15    /// Handled variables:
16    /// - `NODEDB_LITE_MEMORY_MB` — total memory budget in mebibytes (parsed as `usize`)
17    /// - `NODEDB_LITE_AUTO_FLUSH_MS` — auto-flush interval in milliseconds (parsed as `u64`;
18    ///   0 = disabled)
19    /// - `NODEDB_LITE_AUTO_COMPACT_MS` — auto-compact interval in milliseconds (parsed as `u64`;
20    ///   0 = disabled, the default)
21    /// - `NODEDB_LITE_OUTBOUND_QUEUE_CAP` — max pending entries per durable outbound queue
22    ///   (parsed as `usize`; must be > 0)
23    pub fn from_env() -> Self {
24        let mut cfg = Self::default();
25
26        if let Ok(val) = std::env::var("NODEDB_LITE_MEMORY_MB") {
27            match val.trim().parse::<usize>() {
28                Ok(mb) => {
29                    let bytes = mb.saturating_mul(1024 * 1024);
30                    tracing::info!(
31                        env_var = "NODEDB_LITE_MEMORY_MB",
32                        value = mb,
33                        bytes,
34                        "environment variable override applied"
35                    );
36                    cfg.memory_budget = bytes;
37                }
38                Err(_) => {
39                    tracing::warn!(
40                        env_var = "NODEDB_LITE_MEMORY_MB",
41                        value = %val,
42                        "ignoring malformed environment variable (expected unsigned integer), \
43                         using default 100 MiB"
44                    );
45                }
46            }
47        }
48
49        if let Ok(val) = std::env::var("NODEDB_LITE_OUTBOUND_QUEUE_CAP") {
50            match val.trim().parse::<usize>() {
51                Ok(cap) if cap > 0 => {
52                    tracing::info!(
53                        env_var = "NODEDB_LITE_OUTBOUND_QUEUE_CAP",
54                        value = cap,
55                        "environment variable override applied"
56                    );
57                    cfg.outbound_queue_cap = cap;
58                }
59                Ok(_) => {
60                    tracing::warn!(
61                        env_var = "NODEDB_LITE_OUTBOUND_QUEUE_CAP",
62                        "value must be > 0; using default 100_000"
63                    );
64                }
65                Err(_) => {
66                    tracing::warn!(
67                        env_var = "NODEDB_LITE_OUTBOUND_QUEUE_CAP",
68                        value = %val,
69                        "ignoring malformed environment variable (expected unsigned integer), \
70                         using default 100_000"
71                    );
72                }
73            }
74        }
75
76        if let Ok(val) = std::env::var("NODEDB_LITE_AUTO_FLUSH_MS") {
77            match val.trim().parse::<u64>() {
78                Ok(ms) => {
79                    tracing::info!(
80                        env_var = "NODEDB_LITE_AUTO_FLUSH_MS",
81                        value = ms,
82                        "environment variable override applied"
83                    );
84                    cfg.auto_flush_ms = ms;
85                }
86                Err(_) => {
87                    tracing::warn!(
88                        env_var = "NODEDB_LITE_AUTO_FLUSH_MS",
89                        value = %val,
90                        "ignoring malformed environment variable (expected unsigned integer), \
91                         using default 1000 ms"
92                    );
93                }
94            }
95        }
96
97        if let Ok(val) = std::env::var("NODEDB_LITE_AUTO_COMPACT_MS") {
98            match val.trim().parse::<u64>() {
99                Ok(ms) => {
100                    tracing::info!(
101                        env_var = "NODEDB_LITE_AUTO_COMPACT_MS",
102                        value = ms,
103                        "environment variable override applied"
104                    );
105                    cfg.auto_compact_ms = ms;
106                }
107                Err(_) => {
108                    tracing::warn!(
109                        env_var = "NODEDB_LITE_AUTO_COMPACT_MS",
110                        value = %val,
111                        "ignoring malformed environment variable (expected unsigned integer), \
112                         using default 0 (disabled)"
113                    );
114                }
115            }
116        }
117
118        cfg
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    /// All `from_env` cases run sequentially in one test to avoid parallel
127    /// env-var mutation across threads (no `serial_test` dependency needed).
128    #[test]
129    fn from_env_all_cases() {
130        // Use a mutex so if other test files ever share this process they
131        // cannot race on the env var.
132        static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
133        let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
134
135        // SAFETY: we hold ENV_LOCK and are the only thread touching this var.
136
137        // Case 1: var absent → default.
138        unsafe { std::env::remove_var("NODEDB_LITE_MEMORY_MB") };
139        let cfg = LiteConfig::from_env();
140        assert_eq!(
141            cfg.memory_budget,
142            100 * 1024 * 1024,
143            "absent var should give default 100 MiB"
144        );
145
146        // Case 2: valid integer → applied.
147        unsafe { std::env::set_var("NODEDB_LITE_MEMORY_MB", "256") };
148        let cfg = LiteConfig::from_env();
149        assert_eq!(
150            cfg.memory_budget,
151            256 * 1024 * 1024,
152            "256 MiB should be applied"
153        );
154
155        // Case 3: malformed → fallback to default.
156        unsafe { std::env::set_var("NODEDB_LITE_MEMORY_MB", "not_a_number") };
157        let cfg = LiteConfig::from_env();
158        assert_eq!(
159            cfg.memory_budget,
160            100 * 1024 * 1024,
161            "malformed var should fall back to default"
162        );
163
164        // Case 4: whitespace-padded integer → trimmed and applied.
165        unsafe { std::env::set_var("NODEDB_LITE_MEMORY_MB", "  512  ") };
166        let cfg = LiteConfig::from_env();
167        assert_eq!(
168            cfg.memory_budget,
169            512 * 1024 * 1024,
170            "padded value should be trimmed and applied"
171        );
172
173        // Cleanup.
174        unsafe { std::env::remove_var("NODEDB_LITE_MEMORY_MB") };
175
176        // NODEDB_LITE_AUTO_FLUSH_MS cases.
177
178        // Case A: var absent → default 1000.
179        unsafe { std::env::remove_var("NODEDB_LITE_AUTO_FLUSH_MS") };
180        let cfg = LiteConfig::from_env();
181        assert_eq!(
182            cfg.auto_flush_ms, 1_000,
183            "absent var should give default 1000 ms"
184        );
185
186        // Case B: valid integer → applied.
187        unsafe { std::env::set_var("NODEDB_LITE_AUTO_FLUSH_MS", "500") };
188        let cfg = LiteConfig::from_env();
189        assert_eq!(cfg.auto_flush_ms, 500, "500 ms should be applied");
190
191        // Case C: 0 = disabled.
192        unsafe { std::env::set_var("NODEDB_LITE_AUTO_FLUSH_MS", "0") };
193        let cfg = LiteConfig::from_env();
194        assert_eq!(cfg.auto_flush_ms, 0, "0 should disable auto-flush");
195
196        // Case D: malformed → fallback to default.
197        unsafe { std::env::set_var("NODEDB_LITE_AUTO_FLUSH_MS", "not_a_number") };
198        let cfg = LiteConfig::from_env();
199        assert_eq!(
200            cfg.auto_flush_ms, 1_000,
201            "malformed var should fall back to default 1000 ms"
202        );
203
204        // Cleanup.
205        unsafe { std::env::remove_var("NODEDB_LITE_AUTO_FLUSH_MS") };
206
207        // NODEDB_LITE_AUTO_COMPACT_MS cases.
208
209        // Case A: var absent → default 0 (disabled).
210        unsafe { std::env::remove_var("NODEDB_LITE_AUTO_COMPACT_MS") };
211        let cfg = LiteConfig::from_env();
212        assert_eq!(
213            cfg.auto_compact_ms, 0,
214            "absent var should give default 0 (disabled)"
215        );
216
217        // Case B: valid integer → applied.
218        unsafe { std::env::set_var("NODEDB_LITE_AUTO_COMPACT_MS", "300000") };
219        let cfg = LiteConfig::from_env();
220        assert_eq!(cfg.auto_compact_ms, 300_000, "300000 ms should be applied");
221
222        // Case C: malformed → fallback to default 0.
223        unsafe { std::env::set_var("NODEDB_LITE_AUTO_COMPACT_MS", "not_a_number") };
224        let cfg = LiteConfig::from_env();
225        assert_eq!(
226            cfg.auto_compact_ms, 0,
227            "malformed var should fall back to default 0"
228        );
229
230        // Cleanup.
231        unsafe { std::env::remove_var("NODEDB_LITE_AUTO_COMPACT_MS") };
232    }
233}