llm_optimizer_processor/state/
memory.rs1use async_trait::async_trait;
8use chrono::{DateTime, Utc};
9use dashmap::DashMap;
10use std::sync::Arc;
11use std::time::Duration;
12use tokio::sync::RwLock;
13use tracing::{debug, trace, warn};
14
15use super::backend::StateBackend;
16use crate::error::StateResult;
17
18#[derive(Debug, Clone)]
20struct StateEntry {
21 value: Vec<u8>,
23 created_at: DateTime<Utc>,
25 accessed_at: DateTime<Utc>,
27 modified_at: DateTime<Utc>,
29}
30
31impl StateEntry {
32 fn new(value: Vec<u8>) -> Self {
34 let now = Utc::now();
35 Self {
36 value,
37 created_at: now,
38 accessed_at: now,
39 modified_at: now,
40 }
41 }
42
43 fn is_expired(&self, ttl: Duration) -> bool {
45 let age = Utc::now().signed_duration_since(self.modified_at);
46 age.num_milliseconds() as u64 > ttl.as_millis() as u64
47 }
48
49 fn touch(&mut self) {
51 self.accessed_at = Utc::now();
52 }
53
54 fn update(&mut self, new_value: Vec<u8>) {
56 self.value = new_value;
57 self.modified_at = Utc::now();
58 self.accessed_at = Utc::now();
59 }
60}
61
62#[derive(Debug, Clone, Default)]
64pub struct MemoryBackendStats {
65 pub total_entries: usize,
67 pub expired_entries: usize,
69 pub total_bytes: usize,
71 pub get_count: u64,
73 pub put_count: u64,
75 pub delete_count: u64,
77 pub hit_count: u64,
79 pub miss_count: u64,
81}
82
83pub struct MemoryStateBackend {
128 data: Arc<DashMap<Vec<u8>, StateEntry>>,
130 ttl: Option<Duration>,
132 stats: Arc<RwLock<MemoryBackendStats>>,
134}
135
136impl MemoryStateBackend {
137 pub fn new(ttl: Option<Duration>) -> Self {
160 Self {
161 data: Arc::new(DashMap::new()),
162 ttl,
163 stats: Arc::new(RwLock::new(MemoryBackendStats::default())),
164 }
165 }
166
167 pub async fn stats(&self) -> MemoryBackendStats {
173 self.stats.read().await.clone()
174 }
175
176 pub async fn cleanup_expired(&self) -> usize {
201 let ttl = match self.ttl {
202 Some(ttl) => ttl,
203 None => return 0, };
205
206 let mut removed = 0;
207 let keys_to_remove: Vec<Vec<u8>> = self
208 .data
209 .iter()
210 .filter_map(|entry| {
211 if entry.value().is_expired(ttl) {
212 Some(entry.key().clone())
213 } else {
214 None
215 }
216 })
217 .collect();
218
219 for key in keys_to_remove {
220 self.data.remove(&key);
221 removed += 1;
222 }
223
224 if removed > 0 {
225 debug!("Cleaned up {} expired entries", removed);
226 let mut stats = self.stats.write().await;
227 stats.expired_entries += removed;
228 }
229
230 removed
231 }
232
233 pub fn start_cleanup_task(&self, interval: Duration) -> tokio::task::JoinHandle<()> {
262 let backend = self.clone();
263 tokio::spawn(async move {
264 let mut interval = tokio::time::interval(interval);
265 loop {
266 interval.tick().await;
267 let removed = backend.cleanup_expired().await;
268 if removed > 0 {
269 debug!("Background cleanup removed {} entries", removed);
270 }
271 }
272 })
273 }
274
275 pub async fn checkpoint(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
283 self.data
284 .iter()
285 .map(|entry| (entry.key().clone(), entry.value().value.clone()))
286 .collect()
287 }
288
289 pub async fn restore(&self, checkpoint: Vec<(Vec<u8>, Vec<u8>)>) -> StateResult<()> {
297 self.data.clear();
298
299 for (key, value) in checkpoint {
300 self.data.insert(key, StateEntry::new(value));
301 }
302
303 let mut stats = self.stats.write().await;
304 stats.total_entries = self.data.len();
305
306 Ok(())
307 }
308
309 pub async fn memory_usage(&self) -> usize {
315 self.data
316 .iter()
317 .map(|entry| {
318 entry.key().len() + entry.value().value.len() + std::mem::size_of::<StateEntry>()
319 })
320 .sum()
321 }
322}
323
324impl Clone for MemoryStateBackend {
326 fn clone(&self) -> Self {
327 Self {
328 data: Arc::clone(&self.data),
329 ttl: self.ttl,
330 stats: Arc::clone(&self.stats),
331 }
332 }
333}
334
335#[async_trait]
336impl StateBackend for MemoryStateBackend {
337 async fn get(&self, key: &[u8]) -> StateResult<Option<Vec<u8>>> {
338 trace!("Getting key: {:?}", key);
339
340 let mut stats = self.stats.write().await;
341 stats.get_count += 1;
342
343 let result = if let Some(mut entry) = self.data.get_mut(key) {
345 if let Some(ttl) = self.ttl {
347 if entry.is_expired(ttl) {
348 warn!("Key expired: {:?}", key);
349 drop(entry); self.data.remove(key);
351 stats.miss_count += 1;
352 stats.expired_entries += 1;
353 return Ok(None);
354 }
355 }
356
357 entry.touch();
359 stats.hit_count += 1;
360 Some(entry.value.clone())
361 } else {
362 stats.miss_count += 1;
363 None
364 };
365
366 Ok(result)
367 }
368
369 async fn put(&self, key: &[u8], value: &[u8]) -> StateResult<()> {
370 trace!("Putting key: {:?}, value size: {} bytes", key, value.len());
371
372 let mut stats = self.stats.write().await;
373 stats.put_count += 1;
374
375 if let Some(mut entry) = self.data.get_mut(key) {
376 entry.update(value.to_vec());
377 } else {
378 self.data.insert(key.to_vec(), StateEntry::new(value.to_vec()));
379 stats.total_entries += 1;
380 }
381
382 Ok(())
383 }
384
385 async fn delete(&self, key: &[u8]) -> StateResult<()> {
386 trace!("Deleting key: {:?}", key);
387
388 let mut stats = self.stats.write().await;
389 stats.delete_count += 1;
390
391 if self.data.remove(key).is_some() {
392 stats.total_entries = stats.total_entries.saturating_sub(1);
393 }
394
395 Ok(())
396 }
397
398 async fn list_keys(&self, prefix: &[u8]) -> StateResult<Vec<Vec<u8>>> {
399 trace!("Listing keys with prefix: {:?}", prefix);
400
401 let keys: Vec<Vec<u8>> = if prefix.is_empty() {
402 self.data.iter().map(|entry| entry.key().clone()).collect()
403 } else {
404 self.data
405 .iter()
406 .filter(|entry| entry.key().starts_with(prefix))
407 .map(|entry| entry.key().clone())
408 .collect()
409 };
410
411 Ok(keys)
412 }
413
414 async fn clear(&self) -> StateResult<()> {
415 debug!("Clearing all state");
416 self.data.clear();
417
418 let mut stats = self.stats.write().await;
419 stats.total_entries = 0;
420
421 Ok(())
422 }
423
424 async fn count(&self) -> StateResult<usize> {
425 Ok(self.data.len())
426 }
427
428 async fn contains(&self, key: &[u8]) -> StateResult<bool> {
429 Ok(self.data.contains_key(key))
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436 use crate::state::backend::tests::*;
437
438 #[tokio::test]
439 async fn test_memory_backend_basic() {
440 let backend = MemoryStateBackend::new(None);
441 test_backend_basic_ops(backend).await;
442 }
443
444 #[tokio::test]
445 async fn test_memory_backend_list_keys() {
446 let backend = MemoryStateBackend::new(None);
447 test_backend_list_keys(backend).await;
448 }
449
450 #[tokio::test]
451 async fn test_memory_backend_clear() {
452 let backend = MemoryStateBackend::new(None);
453 test_backend_clear(backend).await;
454 }
455
456 #[tokio::test]
457 async fn test_memory_backend_contains() {
458 let backend = MemoryStateBackend::new(None);
459 test_backend_contains(backend).await;
460 }
461
462 #[tokio::test]
463 async fn test_ttl_expiration() {
464 let backend = MemoryStateBackend::new(Some(Duration::from_millis(100)));
465
466 backend.put(b"key1", b"value1").await.unwrap();
467 assert!(backend.get(b"key1").await.unwrap().is_some());
468
469 tokio::time::sleep(Duration::from_millis(150)).await;
471
472 assert!(backend.get(b"key1").await.unwrap().is_none());
474 }
475
476 #[tokio::test]
477 async fn test_cleanup_expired() {
478 let backend = MemoryStateBackend::new(Some(Duration::from_millis(100)));
479
480 backend.put(b"key1", b"value1").await.unwrap();
481 backend.put(b"key2", b"value2").await.unwrap();
482 backend.put(b"key3", b"value3").await.unwrap();
483
484 assert_eq!(backend.count().await.unwrap(), 3);
485
486 tokio::time::sleep(Duration::from_millis(150)).await;
488
489 let removed = backend.cleanup_expired().await;
490 assert_eq!(removed, 3);
491 assert_eq!(backend.count().await.unwrap(), 0);
492 }
493
494 #[tokio::test]
495 async fn test_checkpoint_restore() {
496 let backend = MemoryStateBackend::new(None);
497
498 backend.put(b"key1", b"value1").await.unwrap();
499 backend.put(b"key2", b"value2").await.unwrap();
500
501 let checkpoint = backend.checkpoint().await;
503 assert_eq!(checkpoint.len(), 2);
504
505 backend.clear().await.unwrap();
507 assert_eq!(backend.count().await.unwrap(), 0);
508
509 backend.restore(checkpoint).await.unwrap();
510 assert_eq!(backend.count().await.unwrap(), 2);
511
512 assert_eq!(
513 backend.get(b"key1").await.unwrap(),
514 Some(b"value1".to_vec())
515 );
516 }
517
518 #[tokio::test]
519 async fn test_stats() {
520 let backend = MemoryStateBackend::new(None);
521
522 backend.put(b"key1", b"value1").await.unwrap();
523 backend.put(b"key2", b"value2").await.unwrap();
524
525 backend.get(b"key1").await.unwrap(); backend.get(b"key1").await.unwrap(); backend.get(b"nonexistent").await.unwrap(); backend.delete(b"key1").await.unwrap();
530
531 let stats = backend.stats().await;
532 assert_eq!(stats.put_count, 2);
533 assert_eq!(stats.get_count, 3);
534 assert_eq!(stats.delete_count, 1);
535 assert_eq!(stats.hit_count, 2);
536 assert_eq!(stats.miss_count, 1);
537 }
538
539 #[tokio::test]
540 async fn test_memory_usage() {
541 let backend = MemoryStateBackend::new(None);
542
543 let initial_usage = backend.memory_usage().await;
544 assert_eq!(initial_usage, 0);
545
546 backend.put(b"key1", b"value1").await.unwrap();
547 let usage_after_put = backend.memory_usage().await;
548 assert!(usage_after_put > initial_usage);
549
550 backend.delete(b"key1").await.unwrap();
551 let usage_after_delete = backend.memory_usage().await;
552 assert_eq!(usage_after_delete, 0);
553 }
554
555 #[tokio::test]
556 async fn test_concurrent_access() {
557 use std::sync::Arc;
558
559 let backend = Arc::new(MemoryStateBackend::new(None));
560 let mut handles = vec![];
561
562 for i in 0..10 {
564 let backend = Arc::clone(&backend);
565 let handle = tokio::spawn(async move {
566 for j in 0..10 {
567 let key = format!("key:{}:{}", i, j).into_bytes();
568 let value = format!("value:{}:{}", i, j).into_bytes();
569 backend.put(&key, &value).await.unwrap();
570 }
571 });
572 handles.push(handle);
573 }
574
575 for handle in handles {
577 handle.await.unwrap();
578 }
579
580 assert_eq!(backend.count().await.unwrap(), 100);
582 }
583}