1use std::sync::{
7 Arc,
8 atomic::{AtomicU64, Ordering},
9};
10
11use anyhow::{Context, Result};
12use serde::{Deserialize, Serialize};
13use tracing::{debug, instrument, warn};
14use wasmtime::{Memory, MemoryType};
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct MemoryLimits {
19 pub max_memory_mb:u64,
21 pub initial_memory_mb:u64,
23 pub max_table_size:u32,
25 pub max_memories:usize,
27 pub max_tables:usize,
29 pub max_instances:usize,
31}
32
33impl Default for MemoryLimits {
34 fn default() -> Self {
35 Self {
36 max_memory_mb:512,
37 initial_memory_mb:64,
38 max_table_size:1024,
39 max_memories:10,
40 max_tables:10,
41 max_instances:100,
42 }
43 }
44}
45
46impl MemoryLimits {
47 pub fn new(max_memory_mb:u64, initial_memory_mb:u64, max_instances:usize) -> Self {
49 Self { max_memory_mb, initial_memory_mb, max_instances, ..Default::default() }
50 }
51
52 pub fn max_memory_bytes(&self) -> u64 { self.max_memory_mb * 1024 * 1024 }
54
55 pub fn initial_memory_bytes(&self) -> u64 { self.initial_memory_mb * 1024 * 1024 }
57
58 pub fn validate_request(&self, requested_bytes:u64, current_usage:u64) -> Result<()> {
60 if current_usage + requested_bytes > self.max_memory_bytes() {
61 return Err(anyhow::anyhow!(
62 "Memory request exceeds limit: {} + {} > {} bytes",
63 current_usage,
64 requested_bytes,
65 self.max_memory_bytes()
66 ));
67 }
68 Ok(())
69 }
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct MemoryAllocation {
75 pub id:String,
77 pub instance_id:String,
79 pub memory_type:String,
81 pub size_bytes:u64,
83 pub max_size_bytes:u64,
85 pub allocated_at:u64,
87 pub is_shared:bool,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct MemoryStats {
94 pub total_allocated:u64,
96 pub total_allocated_mb:f64,
98 pub allocation_count:usize,
100 pub deallocation_count:usize,
102 pub peak_memory_bytes:u64,
104 pub peak_memory_mb:f64,
106}
107
108impl Default for MemoryStats {
109 fn default() -> Self {
110 Self {
111 total_allocated:0,
112 total_allocated_mb:0.0,
113 allocation_count:0,
114 deallocation_count:0,
115 peak_memory_bytes:0,
116 peak_memory_mb:0.0,
117 }
118 }
119}
120
121impl MemoryStats {
122 pub fn record_allocation(&mut self, size_bytes:u64) {
124 self.total_allocated += size_bytes;
125 self.allocation_count += 1;
126 if self.total_allocated > self.peak_memory_bytes {
127 self.peak_memory_bytes = self.total_allocated;
128 }
129 self.total_allocated_mb = self.total_allocated as f64 / (1024.0 * 1024.0);
130 self.peak_memory_mb = self.peak_memory_bytes as f64 / (1024.0 * 1024.0);
131 }
132
133 pub fn record_deallocation(&mut self, size_bytes:u64) {
135 self.total_allocated = self.total_allocated.saturating_sub(size_bytes);
136 self.deallocation_count += 1;
137 self.total_allocated_mb = self.total_allocated as f64 / (1024.0 * 1024.0);
138 }
139}
140
141#[derive(Debug)]
143pub struct MemoryManagerImpl {
144 limits:MemoryLimits,
145 allocations:Vec<MemoryAllocation>,
146 stats:Arc<MemoryStats>,
147 peak_usage:Arc<AtomicU64>,
148}
149
150impl MemoryManagerImpl {
151 pub fn new(limits:MemoryLimits) -> Self {
153 Self {
154 limits,
155 allocations:Vec::new(),
156 stats:Arc::new(MemoryStats::default()),
157 peak_usage:Arc::new(AtomicU64::new(0)),
158 }
159 }
160
161 pub fn limits(&self) -> &MemoryLimits { &self.limits }
163
164 pub fn stats(&self) -> &MemoryStats { &self.stats }
166
167 pub fn peak_usage_bytes(&self) -> u64 { self.peak_usage.load(Ordering::Relaxed) }
169
170 pub fn peak_usage_mb(&self) -> f64 { self.peak_usage.load(Ordering::Relaxed) as f64 / (1024.0 * 1024.0) }
172
173 pub fn current_usage_bytes(&self) -> u64 { self.allocations.iter().map(|a| a.size_bytes).sum() }
175
176 pub fn current_usage_mb(&self) -> f64 { self.current_usage_bytes() as f64 / (1024.0 * 1024.0) }
178
179 pub fn can_allocate(&self, requested_bytes:u64) -> bool {
181 let current = self.current_usage_bytes();
182 current + requested_bytes <= self.limits.max_memory_bytes()
183 }
184
185 #[instrument(skip(self, instance_id))]
187 pub fn allocate_memory(&mut self, instance_id:&str, memory_type:&str, requested_bytes:u64) -> Result<u64> {
188 debug!(
189 "Allocating {} bytes for instance {} (type: {})",
190 requested_bytes, instance_id, memory_type
191 );
192
193 let current_usage = self.current_usage_bytes();
194
195 self.limits
197 .validate_request(requested_bytes, current_usage)
198 .context("Memory allocation validation failed")?;
199
200 if self.allocations.len() >= self.limits.max_memories {
202 return Err(anyhow::anyhow!(
203 "Maximum number of memory allocations reached: {}",
204 self.limits.max_memories
205 ));
206 }
207
208 let allocation = MemoryAllocation {
210 id:format!("alloc-{}", uuid::Uuid::new_v4()),
211 instance_id:instance_id.to_string(),
212 memory_type:memory_type.to_string(),
213 size_bytes:requested_bytes,
214 max_size_bytes:self.limits.max_memory_bytes() - current_usage,
215 allocated_at:std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_secs(),
216 is_shared:false,
217 };
218
219 self.allocations.push(allocation);
220
221 Arc::make_mut(&mut self.stats).record_allocation(requested_bytes);
223
224 let new_peak = self.current_usage_bytes();
226 let current_peak = self.peak_usage.load(Ordering::Relaxed);
227 if new_peak > current_peak {
228 self.peak_usage.store(new_peak, Ordering::Relaxed);
229 }
230
231 debug!("Memory allocated successfully. Total usage: {} MB", self.current_usage_mb());
232
233 Ok(requested_bytes)
234 }
235
236 #[instrument(skip(self, instance_id))]
238 pub fn deallocate_memory(&mut self, instance_id:&str, memory_id:&str) -> Result<bool> {
239 debug!("Deallocating memory {} for instance {}", memory_id, instance_id);
240
241 let pos = self
242 .allocations
243 .iter()
244 .position(|a| a.instance_id == instance_id && a.id == memory_id);
245
246 if let Some(pos) = pos {
247 let allocation = self.allocations.remove(pos);
248
249 Arc::make_mut(&mut self.stats).record_deallocation(allocation.size_bytes);
251
252 debug!(
253 "Memory deallocated successfully. Remaining usage: {} MB",
254 self.current_usage_mb()
255 );
256
257 Ok(true)
258 } else {
259 warn!("Memory allocation not found: {} for instance {}", memory_id, instance_id);
260 Ok(false)
261 }
262 }
263
264 #[instrument(skip(self, instance_id))]
266 pub fn deallocate_all_for_instance(&mut self, instance_id:&str) -> usize {
267 debug!("Deallocating all memory for instance {}", instance_id);
268
269 let initial_count = self.allocations.len();
270
271 self.allocations.retain(|a| a.instance_id != instance_id);
272
273 let deallocated_count = initial_count - self.allocations.len();
274
275 if deallocated_count > 0 {
276 debug!(
277 "Deallocated {} memory allocations for instance {}",
278 deallocated_count, instance_id
279 );
280 }
281
282 deallocated_count
283 }
284
285 #[instrument(skip(self, instance_id, memory_id))]
287 pub fn grow_memory(&mut self, instance_id:&str, memory_id:&str, additional_bytes:u64) -> Result<u64> {
288 debug!(
289 "Growing memory {} for instance {} by {} bytes",
290 memory_id, instance_id, additional_bytes
291 );
292
293 let current_usage = self.current_usage_bytes();
295
296 let allocation = self
297 .allocations
298 .iter_mut()
299 .find(|a| a.instance_id == instance_id && a.id == memory_id)
300 .ok_or_else(|| anyhow::anyhow!("Memory allocation not found"))?;
301
302 self.limits
304 .validate_request(additional_bytes, current_usage)
305 .context("Memory growth validation failed")?;
306
307 allocation.size_bytes += additional_bytes;
308
309 debug!("Memory grown successfully. New size: {} bytes", allocation.size_bytes);
310
311 Ok(allocation.size_bytes)
312 }
313
314 pub fn get_allocations_for_instance(&self, instance_id:&str) -> Vec<&MemoryAllocation> {
316 self.allocations.iter().filter(|a| a.instance_id == instance_id).collect()
317 }
318
319 pub fn is_exceeded(&self) -> bool { self.current_usage_bytes() > self.limits.max_memory_bytes() }
321
322 pub fn usage_percentage(&self) -> f64 {
324 (self.current_usage_bytes() as f64 / self.limits.max_memory_bytes() as f64) * 100.0
325 }
326
327 pub fn reset(&mut self) {
329 self.allocations.clear();
330 self.stats = Arc::new(MemoryStats::default());
331 self.peak_usage.store(0, Ordering::Relaxed);
332 debug!("Memory manager reset");
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 #[test]
341 fn test_memory_limits_default() {
342 let limits = MemoryLimits::default();
343 assert_eq!(limits.max_memory_mb, 512);
344 assert_eq!(limits.initial_memory_mb, 64);
345 }
346
347 #[test]
348 fn test_memory_limits_custom() {
349 let limits = MemoryLimits::new(1024, 128, 50);
350 assert_eq!(limits.max_memory_mb, 1024);
351 assert_eq!(limits.initial_memory_mb, 128);
352 assert_eq!(limits.max_instances, 50);
353 }
354
355 #[test]
356 fn test_memory_limits_validation() {
357 let limits = MemoryLimits::new(100, 10, 10);
358
359 assert!(limits.validate_request(50, 0).is_ok());
361
362 assert!(limits.validate_request(150, 0).is_err());
364 assert!(limits.validate_request(50, 60).is_err());
365 }
366
367 #[test]
368 fn test_memory_manager_creation() {
369 let limits = MemoryLimits::default();
370 let manager = MemoryManagerImpl::new(limits);
371 assert_eq!(manager.current_usage_bytes(), 0);
372 assert_eq!(manager.allocations.len(), 0);
373 }
374
375 #[test]
376 fn test_memory_allocation() {
377 let limits = MemoryLimits::default();
378 let mut manager = MemoryManagerImpl::new(limits);
379
380 let result = manager.allocate_memory("test-instance", "heap", 1024);
381 assert!(result.is_ok());
382 assert_eq!(manager.current_usage_bytes(), 1024);
383 assert_eq!(manager.allocations.len(), 1);
384 }
385
386 #[test]
387 fn test_memory_deallocation() {
388 let limits = MemoryLimits::default();
389 let mut manager = MemoryManagerImpl::new(limits);
390
391 manager.allocate_memory("test-instance", "heap", 1024).unwrap();
392 let allocation = &manager.allocations[0];
393 let memory_id = allocation.id.clone();
394
395 let result = manager.deallocate_memory("test-instance", &memory_id);
396 assert!(result.is_ok());
397 assert_eq!(manager.current_usage_bytes(), 0);
398 assert_eq!(manager.allocations.len(), 0);
399 }
400
401 #[test]
402 fn test_memory_stats() {
403 let mut stats = MemoryStats::default();
404 stats.record_allocation(1024);
405 assert_eq!(stats.allocation_count, 1);
406 assert_eq!(stats.total_allocated, 1024);
407
408 stats.record_deallocation(512);
409 assert_eq!(stats.deallocation_count, 1);
410 assert_eq!(stats.total_allocated, 512);
411 }
412
413 #[test]
414 fn test_memory_usage_percentage() {
415 let limits = MemoryLimits::new(1000, 0, 0);
416 let mut manager = MemoryManagerImpl::new(limits);
417
418 manager.allocate_memory("test", "heap", 500).unwrap();
419 assert_eq!(manager.usage_percentage(), 50.0);
420 }
421}