1use std::time::Duration;
9
10pub mod io {
12 use super::Duration;
13
14 pub const TAIL_FLUSH_INTERVAL: Duration = Duration::from_millis(250);
16
17 pub const FLUSH_LINE_COUNT: u16 = 40;
19
20 pub const LINE_CAPACITY: usize = 512;
22
23 pub const READ_BUFFER_SIZE: usize = 8192;
25
26 pub const OUTPUT_BUFFER_CAPACITY: usize = 1024;
28
29 pub const INITIAL_CHUNK_SIZE: usize = 32 * 1024; }
32
33pub mod processing {
35 pub const LARGE_OFFSET_THRESHOLD: u64 = 10_000;
38
39 pub const CHUNKED_WITH_OFFSET_FILE_SIZE: u64 = 100 * 1024 * 1024; pub const ALWAYS_CHUNKED_FILE_SIZE: u64 = 1024 * 1024 * 1024; pub const ADAPTATION_INTERVAL: usize = 20;
47
48 pub const BLOCK_SIZE: u64 = 512;
50}
51
52pub mod memory {
54 pub const MEMORY_LIMIT_BYTES: usize = 10 * 1024 * 1024; }
57
58pub struct SystemDefaults;
60
61impl SystemDefaults {
62 pub const DEFAULT_MEMORY_PERCENTAGE: f64 = 10.0;
69
70 pub const MIN_MEMORY_BUDGET: usize = 5 * 1024 * 1024; pub const MAX_MEMORY_BUDGET: usize = 500 * 1024 * 1024; pub const DEFAULT_CHUNK_SIZE: usize = 32 * 1024; pub const MIN_CHUNK_SIZE: usize = 4 * 1024; pub const MAX_CHUNK_SIZE: usize = 4 * 1024 * 1024; pub fn optimal_chunk_for_file(file_size: u64) -> usize {
100 const BLOCK_SIZE: usize = 4096;
102
103 let base_size = match file_size {
104 0..=102_400 => Self::MIN_CHUNK_SIZE,
106
107 102_401..=1_048_576 => 8 * 1024,
109
110 1_048_577..=10_485_760 => Self::DEFAULT_CHUNK_SIZE,
112
113 10_485_761..=104_857_600 => 128 * 1024,
115
116 104_857_601..=1_073_741_824 => 512 * 1024,
118
119 _ => 1024 * 1024,
121 };
122
123 base_size.div_ceil(BLOCK_SIZE) * BLOCK_SIZE
125 }
126
127 pub fn default_strategy() -> &'static str {
129 "conservative"
134 }
135
136 pub fn should_chunk_by_default(file_size: u64) -> bool {
138 file_size > 1_048_576
141 }
142
143 pub const DEFAULT_BATCH_WINDOW_MS: u64 = 250;
145
146 pub const DEFAULT_LINE_CAPACITY: usize = 512;
148
149 pub const DEFAULT_OUTPUT_BUFFER_CAPACITY: usize = 4096;
151
152 pub const ADAPTATION_INTERVAL: usize = 20;
154
155 pub const MEMORY_PRESSURE_LOW_THRESHOLD: f64 = 0.60; pub const MEMORY_PRESSURE_MODERATE_THRESHOLD: f64 = 0.85; pub const MEMORY_PRESSURE_HIGH_THRESHOLD: f64 = 0.95; pub const PRESSURE_FACTOR_LOW: f64 = 1.0; pub const PRESSURE_FACTOR_MODERATE: f64 = 0.8; pub const PRESSURE_FACTOR_HIGH: f64 = 0.5; pub const PRESSURE_FACTOR_CRITICAL: f64 = 0.25; pub const EMERGENCY_ALLOCATION_FACTOR: f64 = 0.25; }
170
171pub enum ConfigPreset {
173 LowMemory,
175 Balanced,
177 HighPerformance,
179 Conservative,
181}
182
183pub struct PresetSettings {
185 pub memory_percentage: f64,
186 pub max_memory_mb: usize,
187 pub default_chunk_kb: usize,
188 pub max_chunk_kb: usize,
189 pub strategy: &'static str,
190 pub force_chunked: bool,
191 pub adaptation_interval: usize,
192}
193
194impl ConfigPreset {
195 pub fn settings(&self) -> PresetSettings {
197 match self {
198 ConfigPreset::LowMemory => PresetSettings {
199 memory_percentage: 5.0,
200 max_memory_mb: 50,
201 default_chunk_kb: 16,
202 max_chunk_kb: 256,
203 strategy: "conservative",
204 force_chunked: true,
205 adaptation_interval: 10,
206 },
207
208 ConfigPreset::Balanced => PresetSettings {
209 memory_percentage: 10.0,
210 max_memory_mb: 200,
211 default_chunk_kb: 32,
212 max_chunk_kb: 2048,
213 strategy: "conservative",
214 force_chunked: false,
215 adaptation_interval: 20,
216 },
217
218 ConfigPreset::HighPerformance => PresetSettings {
219 memory_percentage: 20.0,
220 max_memory_mb: 500,
221 default_chunk_kb: 128,
222 max_chunk_kb: 4096,
223 strategy: "adaptive",
224 force_chunked: false,
225 adaptation_interval: 30,
226 },
227
228 ConfigPreset::Conservative => PresetSettings {
229 memory_percentage: 5.0,
230 max_memory_mb: 100,
231 default_chunk_kb: 16,
232 max_chunk_kb: 512,
233 strategy: "static",
234 force_chunked: true,
235 adaptation_interval: 10,
236 },
237 }
238 }
239
240 pub fn auto_detect() -> Self {
242 if let Some(memory_stats) = memory_stats::memory_stats() {
243 let total_memory_mb = memory_stats.physical_mem / (1024 * 1024);
244
245 match total_memory_mb {
246 0..=2048 => ConfigPreset::LowMemory,
248
249 2049..=8192 => ConfigPreset::Conservative,
251
252 8193..=16384 => ConfigPreset::Balanced,
254
255 _ => ConfigPreset::HighPerformance,
257 }
258 } else {
259 ConfigPreset::Balanced
261 }
262 }
263}
264
265pub fn get_system_config() -> PresetSettings {
267 if let Ok(preset_name) = std::env::var("TALE_PRESET") {
269 let preset = match preset_name.to_lowercase().as_str() {
270 "low" | "lowmemory" | "low_memory" => ConfigPreset::LowMemory,
271 "balanced" | "balance" => ConfigPreset::Balanced,
272 "high" | "highperformance" | "high_performance" => ConfigPreset::HighPerformance,
273 "conservative" | "conserve" => ConfigPreset::Conservative,
274 _ => {
275 eprintln!("Warning: Unknown TALE_PRESET '{}', using auto-detection", preset_name);
276 ConfigPreset::auto_detect()
277 }
278 };
279 return preset.settings();
280 }
281
282 ConfigPreset::auto_detect().settings()
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
291 fn chunk_sizes_are_block_aligned() {
292 const BLOCK_SIZE: usize = 4096;
293
294 let tiny = SystemDefaults::optimal_chunk_for_file(50_000);
295 assert_eq!(tiny % BLOCK_SIZE, 0);
296 assert_eq!(tiny, SystemDefaults::MIN_CHUNK_SIZE);
297
298 let small = SystemDefaults::optimal_chunk_for_file(500_000);
299 assert_eq!(small % BLOCK_SIZE, 0);
300 assert_eq!(small, 8 * 1024);
301
302 let medium = SystemDefaults::optimal_chunk_for_file(5_000_000);
303 assert_eq!(medium % BLOCK_SIZE, 0);
304 assert_eq!(medium, SystemDefaults::DEFAULT_CHUNK_SIZE);
305
306 let large = SystemDefaults::optimal_chunk_for_file(50_000_000);
307 assert_eq!(large % BLOCK_SIZE, 0);
308 assert_eq!(large, 128 * 1024);
309
310 let huge = SystemDefaults::optimal_chunk_for_file(2_000_000_000);
311 assert_eq!(huge % BLOCK_SIZE, 0);
312 assert_eq!(huge, 1024 * 1024);
313 }
314
315 #[test]
316 fn chunking_decisions_are_good() {
317 assert!(!SystemDefaults::should_chunk_by_default(100_000));
319
320 assert!(SystemDefaults::should_chunk_by_default(2_000_000));
322 }
323
324 #[test]
325 fn presets_are_as_expected() {
326 let low_mem = ConfigPreset::LowMemory.settings();
327 assert_eq!(low_mem.memory_percentage, 5.0);
328 assert_eq!(low_mem.strategy, "conservative");
329 assert!(low_mem.force_chunked);
330
331 let balanced = ConfigPreset::Balanced.settings();
332 assert_eq!(balanced.memory_percentage, 10.0);
333 assert_eq!(balanced.strategy, "conservative");
334 assert!(!balanced.force_chunked);
335
336 let high_perf = ConfigPreset::HighPerformance.settings();
337 assert_eq!(high_perf.memory_percentage, 20.0);
338 assert_eq!(high_perf.strategy, "adaptive");
339 assert!(!high_perf.force_chunked);
340 }
341
342 #[test]
343 fn we_have_processing_constants() {
344 use super::processing::*;
345
346 assert_eq!(LARGE_OFFSET_THRESHOLD, 10_000);
348 assert_eq!(CHUNKED_WITH_OFFSET_FILE_SIZE, 100 * 1024 * 1024);
349 assert_eq!(ALWAYS_CHUNKED_FILE_SIZE, 1024 * 1024 * 1024);
350 assert_eq!(ADAPTATION_INTERVAL, 20);
351 }
352
353 #[test]
354 fn we_have_io_constants() {
355 use super::io::*;
356
357 assert_eq!(TAIL_FLUSH_INTERVAL.as_millis(), 250);
359 assert_eq!(FLUSH_LINE_COUNT, 40);
360 assert_eq!(READ_BUFFER_SIZE, 8192);
361 assert_eq!(INITIAL_CHUNK_SIZE, 32 * 1024);
362 }
363}