1use lru::LruCache;
4use parking_lot::RwLock;
5use std::collections::HashMap;
6use std::num::NonZeroUsize;
7use std::sync::Arc;
8
9use crate::{types::TableId, Config, Result, Value};
10
11#[derive(Debug)]
13pub struct MemoryManager {
14 block_cache: Arc<RwLock<BlockCache>>,
16
17 row_cache: Arc<RwLock<RowCache>>,
19
20 buffer_pool: Arc<RwLock<BufferPool>>,
22
23 stats: Arc<RwLock<MemoryStats>>,
25}
26
27struct BlockCache {
29 cache: LruCache<BlockKey, Arc<Block>>,
31
32 max_size: usize,
34
35 current_size: usize,
37}
38
39struct RowCache {
41 cache: LruCache<RowKey, Arc<CachedRow>>,
43
44 max_size: usize,
46
47 current_size: usize,
49}
50
51#[derive(Debug)]
53struct BufferPool {
54 free_buffers: HashMap<usize, Vec<Vec<u8>>>,
56
57 allocated_count: usize,
59
60 total_memory: usize,
62
63 max_memory: usize,
65}
66
67#[derive(Debug, Clone, Hash, PartialEq, Eq)]
69struct BlockKey {
70 table_id: TableId,
71 block_id: u64,
72}
73
74#[derive(Debug, Clone, Hash, PartialEq, Eq)]
76struct RowKey {
77 table_id: TableId,
78 row_key: String,
79}
80
81#[derive(Debug)]
83struct Block {
84 size: usize,
86
87 _last_access: std::time::Instant,
89}
90
91#[derive(Debug)]
93struct CachedRow {
94 _data: Vec<Value>,
96
97 size: usize,
99}
100
101impl MemoryManager {
102 pub fn new(config: &Config) -> Result<Self> {
104 let block_cache = Arc::new(RwLock::new(BlockCache::new(
105 config.memory.block_cache.max_size as usize,
106 )));
107 let row_cache = Arc::new(RwLock::new(RowCache::new(
108 config.memory.row_cache.max_size as usize,
109 )));
110 let buffer_pool = Arc::new(RwLock::new(BufferPool::new(
111 config.memory.max_memory as usize,
112 )));
113
114 Ok(Self {
115 block_cache,
116 row_cache,
117 buffer_pool,
118 stats: Arc::new(RwLock::new(MemoryStats::default())),
119 })
120 }
121
122 pub fn get_block(&self, table_id: &TableId, block_id: u64) -> Option<Arc<Block>> {
124 let key = BlockKey {
125 table_id: table_id.clone(),
126 block_id,
127 };
128
129 let mut cache = self.block_cache.write();
130
131 if let Some(block) = cache.cache.get(&key) {
133 {
135 let mut stats = self.stats.write();
136 stats.block_cache_hits += 1;
137 }
138
139 Some(Arc::clone(block))
140 } else {
141 {
143 let mut stats = self.stats.write();
144 stats.block_cache_misses += 1;
145 }
146
147 None
148 }
149 }
150
151 pub fn put_block(&self, table_id: &TableId, block_id: u64, data: Vec<u8>) {
153 let key = BlockKey {
154 table_id: table_id.clone(),
155 block_id,
156 };
157
158 let block = Arc::new(Block {
159 size: data.len(),
160 _last_access: std::time::Instant::now(),
161 });
162
163 let mut cache = self.block_cache.write();
164
165 while cache.current_size + block.size > cache.max_size {
167 if let Some((_, evicted_block)) = cache.cache.pop_lru() {
169 cache.current_size -= evicted_block.size;
170 } else {
171 break;
173 }
174 }
175
176 cache.current_size += block.size;
178 cache.cache.put(key, block);
179 }
180
181 pub fn get_row(&self, table_id: &TableId, row_key: &str) -> Option<Arc<CachedRow>> {
183 let key = RowKey {
184 table_id: table_id.clone(),
185 row_key: row_key.to_string(),
186 };
187
188 let mut cache = self.row_cache.write();
189
190 if let Some(row) = cache.cache.get(&key) {
192 {
194 let mut stats = self.stats.write();
195 stats.row_cache_hits += 1;
196 }
197
198 Some(Arc::clone(row))
199 } else {
200 {
202 let mut stats = self.stats.write();
203 stats.row_cache_misses += 1;
204 }
205
206 None
207 }
208 }
209
210 pub fn put_row(&self, table_id: &TableId, row_key: &str, data: Vec<Value>) {
212 let key = RowKey {
213 table_id: table_id.clone(),
214 row_key: row_key.to_string(),
215 };
216
217 let size = self.estimate_row_size(&data);
218 let row = Arc::new(CachedRow { _data: data, size });
219
220 let mut cache = self.row_cache.write();
221
222 while cache.current_size + row.size > cache.max_size {
224 if let Some((_, evicted_row)) = cache.cache.pop_lru() {
226 cache.current_size -= evicted_row.size;
227 } else {
228 break;
230 }
231 }
232
233 cache.current_size += row.size;
235 cache.cache.put(key, row);
236 }
237
238 pub fn allocate_buffer(&self, size: usize) -> Result<Vec<u8>> {
240 let mut pool = self.buffer_pool.write();
241
242 if let Some(buffers) = pool.free_buffers.get_mut(&size) {
243 if let Some(buffer) = buffers.pop() {
244 pool.allocated_count += 1;
245 pool.total_memory += size;
246
247 let mut stats = self.stats.write();
249 stats.buffer_allocations += 1;
250 stats.total_memory_used = pool.total_memory;
251
252 return Ok(buffer);
253 }
254 }
255
256 if pool.total_memory + size > pool.max_memory {
258 return Err(crate::Error::Memory(format!(
259 "Memory limit exceeded: requested {} bytes would exceed limit of {} bytes (current usage: {} bytes)",
260 size, pool.max_memory, pool.total_memory
261 )));
262 }
263
264 pool.allocated_count += 1;
266 pool.total_memory += size;
267
268 let mut stats = self.stats.write();
270 stats.buffer_allocations += 1;
271 stats.total_memory_used = pool.total_memory;
272
273 Ok(vec![0u8; size])
274 }
275
276 pub fn deallocate_buffer(&self, mut buffer: Vec<u8>) {
278 let size = buffer.len();
279 buffer.clear();
280 buffer.resize(size, 0);
282
283 let mut pool = self.buffer_pool.write();
284 pool.total_memory -= size;
285 pool.free_buffers.entry(size).or_default().push(buffer);
286 pool.allocated_count -= 1;
287
288 let mut stats = self.stats.write();
290 stats.buffer_deallocations += 1;
291 stats.total_memory_used = pool.total_memory;
292 }
293
294 pub fn stats(&self) -> Result<MemoryStats> {
296 let stats = self.stats.read();
297 Ok(stats.clone())
298 }
299
300 pub fn clear_caches(&self) {
302 {
303 let mut cache = self.block_cache.write();
304 cache.cache.clear();
305 cache.current_size = 0;
306 }
307
308 {
309 let mut cache = self.row_cache.write();
310 cache.cache.clear();
311 cache.current_size = 0;
312 }
313 }
314
315 fn estimate_row_size(&self, data: &[Value]) -> usize {
317 estimate_row_size(data)
318 }
319}
320
321pub(crate) fn estimate_row_size(data: &[Value]) -> usize {
331 data.iter().map(estimate_value_size).sum()
332}
333
334pub(crate) fn estimate_value_size(value: &Value) -> usize {
336 match value {
337 Value::Null => 1,
338 Value::Boolean(_) => 1,
339 Value::Integer(_) => 4,
340 Value::BigInt(_) => 8,
341 Value::Counter(_) => 8,
342 Value::Float(_) => 8,
343 Value::Text(s) => s.len(),
344 Value::Blob(b) => b.len(),
345 Value::Timestamp(_) => 8,
346 Value::Date(_) => 4,
347 Value::Time(_) => 8,
348 Value::Uuid(_) => 16,
349 Value::Inet(bytes) => bytes.len(),
350 Value::Json(json) => json.to_string().len(),
351 Value::List(items) => items.iter().map(estimate_value_size).sum(),
352 Value::Map(map) => map
353 .iter()
354 .map(|(k, v)| estimate_value_size(k) + estimate_value_size(v))
355 .sum(),
356 Value::TinyInt(_) => 1,
357 Value::SmallInt(_) => 2,
358 Value::Float32(_) => 4,
359 Value::Set(items) => items.iter().map(estimate_value_size).sum(),
360 Value::Tuple(items) => items.iter().map(estimate_value_size).sum(),
361 Value::Udt(udt) => udt
362 .fields
363 .iter()
364 .map(|f| f.value.as_ref().map_or(0, estimate_value_size))
365 .sum(),
366 Value::Frozen(boxed_value) => estimate_value_size(boxed_value),
367 Value::Varint(data) => data.len(),
368 Value::Decimal { unscaled, .. } => 4 + unscaled.len(), Value::Duration { .. } => 12, Value::Tombstone(_) => 16, }
372}
373
374impl std::fmt::Debug for BlockCache {
375 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376 f.debug_struct("BlockCache")
377 .field("max_size", &self.max_size)
378 .field("current_size", &self.current_size)
379 .field("cache_len", &self.cache.len())
380 .finish()
381 }
382}
383
384impl BlockCache {
385 fn new(max_size: usize) -> Self {
386 let capacity = NonZeroUsize::new(1000).expect("capacity must be non-zero");
390 Self {
391 cache: LruCache::new(capacity),
392 max_size,
393 current_size: 0,
394 }
395 }
396}
397
398impl std::fmt::Debug for RowCache {
399 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
400 f.debug_struct("RowCache")
401 .field("max_size", &self.max_size)
402 .field("current_size", &self.current_size)
403 .field("cache_len", &self.cache.len())
404 .finish()
405 }
406}
407
408impl RowCache {
409 fn new(max_size: usize) -> Self {
410 let capacity = NonZeroUsize::new(1000).expect("capacity must be non-zero");
414 Self {
415 cache: LruCache::new(capacity),
416 max_size,
417 current_size: 0,
418 }
419 }
420}
421
422impl BufferPool {
423 fn new(max_memory: usize) -> Self {
424 Self {
425 free_buffers: HashMap::new(),
426 allocated_count: 0,
427 total_memory: 0,
428 max_memory,
429 }
430 }
431}
432
433#[derive(Debug, Clone, Default)]
435pub struct MemoryStats {
436 pub block_cache_hits: u64,
438
439 pub block_cache_misses: u64,
441
442 pub row_cache_hits: u64,
444
445 pub row_cache_misses: u64,
447
448 pub total_memory_used: usize,
450
451 pub buffer_allocations: u64,
453
454 pub buffer_deallocations: u64,
456}
457
458impl MemoryStats {
459 pub fn block_cache_hit_rate(&self) -> f64 {
461 let total = self.block_cache_hits + self.block_cache_misses;
462 if total > 0 {
463 self.block_cache_hits as f64 / total as f64
464 } else {
465 0.0
466 }
467 }
468
469 pub fn row_cache_hit_rate(&self) -> f64 {
471 let total = self.row_cache_hits + self.row_cache_misses;
472 if total > 0 {
473 self.row_cache_hits as f64 / total as f64
474 } else {
475 0.0
476 }
477 }
478}
479
480#[cfg(test)]
481mod tests {
482 use super::*;
483 use crate::types::TableId;
484
485 #[test]
486 fn test_memory_manager_creation() {
487 let config = Config::default();
488 let manager = MemoryManager::new(&config).unwrap();
489
490 let stats = manager.stats().unwrap();
491 assert_eq!(stats.block_cache_hits, 0);
492 assert_eq!(stats.block_cache_misses, 0);
493 }
494
495 #[test]
496 fn test_block_cache() {
497 let config = Config::default();
498 let manager = MemoryManager::new(&config).unwrap();
499
500 let table_id = TableId::new("test_table");
501 let block_id = 1;
502 let data = vec![1, 2, 3, 4, 5];
503
504 let result = manager.get_block(&table_id, block_id);
506 assert!(result.is_none());
507
508 manager.put_block(&table_id, block_id, data.clone());
510
511 let result = manager.get_block(&table_id, block_id);
513 assert!(result.is_some());
514 assert_eq!(result.unwrap().size, data.len());
515 }
516
517 #[test]
518 fn test_block_cache_eviction_updates_stats() {
519 let mut config = Config::default();
520 config.memory.block_cache.max_size = 8;
521 let manager = MemoryManager::new(&config).unwrap();
522
523 let table_id = TableId::new("ks_table");
524
525 manager.put_block(&table_id, 1, vec![0u8; 8]);
526 manager.put_block(&table_id, 2, vec![0u8; 4]); assert!(manager.get_block(&table_id, 1).is_none());
529 assert!(manager.get_block(&table_id, 2).is_some());
530
531 let stats = manager.stats().unwrap();
532 assert_eq!(stats.block_cache_hits, 1);
533 assert_eq!(stats.block_cache_misses, 1);
534 }
535
536 #[test]
537 fn test_row_cache() {
538 let config = Config::default();
539 let manager = MemoryManager::new(&config).unwrap();
540
541 let table_id = TableId::new("test_table");
542 let row_key = "test_key";
543 let data = vec![Value::Integer(42), Value::Text("hello".to_string())];
544
545 let result = manager.get_row(&table_id, row_key);
547 assert!(result.is_none());
548
549 manager.put_row(&table_id, row_key, data.clone());
551
552 let result = manager.get_row(&table_id, row_key);
554 assert!(result.is_some());
555 assert_eq!(result.unwrap()._data, data);
556 }
557
558 #[test]
559 fn test_row_cache_eviction_and_stats() {
560 let mut config = Config::default();
561 config.memory.row_cache.max_size = 8;
562 let manager = MemoryManager::new(&config).unwrap();
563
564 let table_id = TableId::new("ks_table");
565
566 manager.put_row(&table_id, "k1", vec![Value::Text("abcd".into())]);
567 manager.put_row(&table_id, "k2", vec![Value::Text("efgh".into())]);
568 manager.put_row(&table_id, "k3", vec![Value::Text("ijkl".into())]);
569
570 assert!(manager.get_row(&table_id, "k1").is_none());
571 assert!(manager.get_row(&table_id, "k3").is_some());
572
573 let stats = manager.stats().unwrap();
574 assert_eq!(stats.row_cache_hits, 1);
575 assert_eq!(stats.row_cache_misses, 1);
576 }
577
578 #[test]
579 fn test_buffer_pool() {
580 let config = Config::default();
581 let manager = MemoryManager::new(&config).unwrap();
582
583 let size = 1024;
584 let buffer = manager.allocate_buffer(size).unwrap();
585 assert_eq!(buffer.len(), size);
586
587 manager.deallocate_buffer(buffer);
588
589 let buffer2 = manager.allocate_buffer(size).unwrap();
591 assert_eq!(buffer2.len(), size);
592 }
593
594 #[test]
595 fn test_clear_caches() {
596 let mut config = Config::default();
597 config.memory.block_cache.max_size = 8;
598 config.memory.row_cache.max_size = 8;
599 let manager = MemoryManager::new(&config).unwrap();
600
601 let table_id = TableId::new("ks_table");
602 manager.put_block(&table_id, 1, vec![0u8; 8]);
603 manager.put_row(&table_id, "k1", vec![Value::Text("abcd".into())]);
604
605 manager.clear_caches();
606
607 assert!(manager.get_block(&table_id, 1).is_none());
608 assert!(manager.get_row(&table_id, "k1").is_none());
609 }
610
611 #[test]
612 fn test_memory_limit_enforcement() {
613 let mut config = Config::default();
614 config.memory.max_memory = 128 * 1024 * 1024; let manager = MemoryManager::new(&config).unwrap();
616
617 let buffer1 = manager
619 .allocate_buffer(64 * 1024 * 1024)
620 .expect("first 64MB should succeed");
621 let buffer2 = manager
622 .allocate_buffer(64 * 1024 * 1024)
623 .expect("second 64MB should succeed");
624
625 let result = manager.allocate_buffer(1024);
627 assert!(result.is_err(), "allocation exceeding limit should fail");
628
629 if let Err(e) = result {
631 let err_msg = e.to_string();
632 assert!(
633 err_msg.contains("Memory limit exceeded"),
634 "error should mention memory limit"
635 );
636 }
637
638 let stats = manager.stats().unwrap();
640 assert_eq!(
641 stats.buffer_allocations, 2,
642 "should have 2 successful allocations"
643 );
644 assert_eq!(
645 stats.total_memory_used,
646 128 * 1024 * 1024,
647 "should be at memory limit"
648 );
649
650 manager.deallocate_buffer(buffer1);
652 let stats = manager.stats().unwrap();
653 assert_eq!(stats.buffer_deallocations, 1);
654 assert_eq!(
655 stats.total_memory_used,
656 64 * 1024 * 1024,
657 "memory should be freed"
658 );
659
660 let buffer3 = manager
662 .allocate_buffer(32 * 1024 * 1024)
663 .expect("allocation after free should succeed");
664
665 manager.deallocate_buffer(buffer2);
667 manager.deallocate_buffer(buffer3);
668
669 let final_stats = manager.stats().unwrap();
670 assert_eq!(
671 final_stats.total_memory_used, 0,
672 "all memory should be freed"
673 );
674 }
675
676 #[test]
677 fn test_memory_limit_with_buffer_reuse() {
678 let mut config = Config::default();
679 config.memory.max_memory = 128 * 1024 * 1024; let manager = MemoryManager::new(&config).unwrap();
681
682 let buffer1 = manager
684 .allocate_buffer(64 * 1024 * 1024)
685 .expect("first 64MB should succeed");
686 let buffer2 = manager
687 .allocate_buffer(64 * 1024 * 1024)
688 .expect("second 64MB should succeed");
689
690 manager.deallocate_buffer(buffer1);
692
693 let stats = manager.stats().unwrap();
694 assert_eq!(
695 stats.total_memory_used,
696 64 * 1024 * 1024,
697 "should have 64MB in use after deallocation"
698 );
699
700 let buffer3 = manager
702 .allocate_buffer(64 * 1024 * 1024)
703 .expect("reuse should succeed");
704
705 let stats = manager.stats().unwrap();
707 assert_eq!(
708 stats.total_memory_used,
709 128 * 1024 * 1024,
710 "reused buffer should count toward memory limit"
711 );
712
713 let result = manager.allocate_buffer(1024);
715 assert!(
716 result.is_err(),
717 "allocation should fail when limit reached via buffer reuse"
718 );
719
720 if let Err(e) = result {
722 let err_msg = e.to_string();
723 assert!(
724 err_msg.contains("Memory limit exceeded"),
725 "error should mention memory limit"
726 );
727 }
728
729 manager.deallocate_buffer(buffer2);
731 manager.deallocate_buffer(buffer3);
732
733 let final_stats = manager.stats().unwrap();
734 assert_eq!(final_stats.total_memory_used, 0, "all memory freed");
735 }
736}