gs_rust_cache/
lib.rs

1//! Thread safe cache developed using [lru crate](https://crates.io/crates/lru) as its core. 
2//! Supports LRU, positive and negative TTLs and miss handler function.
3//! It is intended to be used using the function `retrieve_or_compute`, which will return the value if it is in the cache,
4//! or compute it using the miss_handler function if it is not.
5//!
6//! ## Example
7//!
8//! ```rust
9//! extern crate gs_rust_cache;
10//! use gs_rust_cache::Cache;
11//! use std::any::Any;
12//! use std::time::Duration;
13//!
14//!fn miss_handler(key: &i32, data: &mut i32, adhoc_code: &mut u8, _: &[&dyn Any]) -> bool {
15//!    // Your Code Here
16//!    *data = 123;
17//!    *adhoc_code = 200;
18//!    true
19//! }
20//! 
21//! fn main() {
22//!     let mut cache = Cache::new(
23//!         3,
24//!         miss_handler,
25//!         Duration::from_millis(200),          
26//!         Duration::from_millis(100),          
27//!     );
28//! 
29//!     let key =  456;
30//!     let (value, adhoc_code, is_hit) = cache.retrieve_or_compute(&key); // first one is calculated
31//!     let (value_1, adhoc_code_1, is_hit_1) = cache.retrieve_or_compute(&key); // afterwards it is retrieved
32//! 
33//!     assert_eq!(value, value_1);
34//!     assert_eq!(adhoc_code, adhoc_code_1);
35//!     assert!(is_hit); // is_hit is false because the value was computed
36//!     assert!(is_hit_1); // is_hit_1 is true because the value was retrieved from the cache
37//! 
38//! }
39//! ```
40
41use lru::{LruCache, DefaultHasher};
42use std::hash::Hash;
43use std::num::NonZeroUsize;
44use std::ops::DerefMut;
45use std::time::{Duration, Instant};
46use std::sync::{Arc, Condvar, Mutex};
47use std::any::Any;
48
49#[derive(Debug, PartialEq, Clone, Copy)]
50enum EntryStatus {
51    AVAILABLE,
52    CALCULATING,
53    READY,
54    FAILED,
55}
56
57#[derive(Debug, Clone)]
58struct Entry<D> {
59    data: D,
60    adhoc_code: u8,
61    expiration: Instant,
62    status: EntryStatus,
63    cond_var: Arc<Condvar>
64}
65
66impl<D: PartialEq> PartialEq for Entry<D> {
67    fn eq(&self, other: &Self) -> bool {
68        self.data == other.data &&
69        self.adhoc_code == other.adhoc_code &&
70        self.expiration == other.expiration &&
71        self.status == other.status
72    }
73}
74
75
76impl<D: Default> Entry<D> {
77
78    fn default() -> Self {
79        Entry {
80            data: Default::default(),
81            expiration: Instant::now(),
82            adhoc_code: 0,
83            status: EntryStatus::AVAILABLE,
84            cond_var: Arc::new(Condvar::new())
85        }
86    }
87
88
89    fn new(data: D, expiration: Instant, adhoc_code: u8) -> Self {
90        Entry {
91            data,
92            expiration,
93            adhoc_code,
94            status: EntryStatus::AVAILABLE,
95            cond_var: Arc::new(Condvar::new())
96        }
97    }
98
99    fn is_valid(&self) -> bool {
100        self.expiration > Instant::now()
101    }
102
103}
104
105pub type MissHandler<K, D> = fn(&K, &mut D, &mut u8, &[&dyn Any]) -> bool;
106
107pub struct Cache<K, D> {
108    lru_cache: Arc<Mutex<LruCache<K, Arc<Mutex<Entry<D>>>>>>,
109    miss_handler: MissHandler<K, D>,
110    positive_ttl: Duration, // seconds
111    negative_ttl: Duration, // seconds
112}
113
114impl<K: Eq + Hash + Clone, D: Default + Clone> Cache<K, D> {
115    pub fn new(
116        size: usize,
117        miss_handler: MissHandler<K, D>,
118        positive_ttl: Duration,
119        negative_ttl: Duration,
120    ) -> Self {
121        let hash_builder = DefaultHasher::default();
122        Cache {
123            lru_cache: Arc::new(Mutex::new(LruCache::with_hasher(
124                NonZeroUsize::new(size).unwrap(),
125                hash_builder,
126            ))),
127            miss_handler,
128            positive_ttl,
129            negative_ttl,
130        }
131    }
132
133    pub fn insert(&self, key: &K, data: D) {
134        let expiration = Instant::now() + self.positive_ttl;
135        let entry = Entry::new(data, expiration, 0);
136        let entry_arc = Arc::new(Mutex::new(entry));
137        self.lru_cache.lock().unwrap().put(key.clone(), entry_arc);     
138    }
139
140    pub fn get(&self, key: &K) -> Option<D> {
141        if let Some(entry_arc) = self.get_entry(&key) {
142            let entry = entry_arc.lock().unwrap();
143            return Some(entry.data.clone());
144        }
145        None
146    }
147
148    pub fn update(&self, key: &K, data: D) -> bool {
149        if let Some(entry_arc) = self.get_entry(&key) {
150            let mut entry = entry_arc.lock().unwrap();
151            entry.data = data;
152            entry.expiration = Instant::now() + self.positive_ttl;
153            entry.cond_var.notify_all(); // notify any waiting threads
154            return true;
155        }
156        false
157    }
158
159    fn get_entry(&self, key: &K) -> Option<Arc<Mutex<Entry<D>>>> {
160        // lock the cache
161        let cache_entry = {
162            let mut cache = self.lru_cache.lock().unwrap();
163            cache.get(key).map(|entry| Arc::clone(entry))
164        };
165        // check if the entry exists and is valid
166        match cache_entry {
167            None => return None,
168            Some(entry_arc) => {
169                let entry = entry_arc.lock().unwrap();
170                if entry.is_valid() {
171                    return Some(Arc::clone(&entry_arc));
172                }
173            }            
174        }
175        // if the entry is not valid or does not exist, remove it
176        self.lru_cache.lock().unwrap().pop(key);
177        None
178    }
179
180    pub fn len(&self) -> usize {
181        self.lru_cache.lock().unwrap().len()
182    }
183
184    fn handle_hit(&self, key: &K) -> Option<(D, u8)> {
185        // check if the the entry exists and is valid
186        if let Some(entry_arc) = self.get_entry(key) {
187            let entry_arc_clone = Arc::clone(&entry_arc);
188            let entry = entry_arc.lock().unwrap();
189            match entry.status {
190                EntryStatus::AVAILABLE => {
191                    // should not happen
192                    eprintln!("Error: entry should not be available at this point");
193                    return None;
194                }
195                EntryStatus::CALCULATING => {
196                    println!("CALCULATING");
197                    match entry.cond_var.wait_while(
198                        entry_arc_clone.lock().unwrap(), 
199                        |entry: &mut Entry<D>| entry.status == EntryStatus::CALCULATING)
200                        {
201                            Ok(_) => {}
202                            Err(e) => {
203                                eprintln!("Error while waiting: {:?}", e);
204                                return None;
205                            }
206                        }
207                }
208                _ => {}
209            }
210            return Some((entry.data.clone(), entry.adhoc_code));
211        }
212        return None;
213    }
214
215    pub fn retrieve_or_compute(&self, key: &K) -> Option<(D, u8, bool)> {
216        self.retrieve_or_compute_with_params(key, &[])
217    }
218
219    pub fn retrieve_or_compute_with_params(&self, key: &K, params: &[&dyn Any]) -> Option<(D, u8, bool)> {
220        let miss_handler = self.miss_handler;
221        let positive_ttl = self.positive_ttl;
222        let negative_ttl = self.negative_ttl;
223
224        if let Some((data, adhoc_code)) = self.handle_hit(&key) {
225            return Some((data, adhoc_code, true));
226        }
227
228        // Miss
229        let entry_arc = {
230            let mut locked_cache = self.lru_cache.lock().unwrap();
231            let entry_arc = locked_cache.get_or_insert_mut(key.clone(), || Arc::new(Mutex::new(Entry::default())));
232            Arc::clone(&entry_arc)
233        };
234
235        let mut locked_entry = entry_arc.lock().unwrap();
236        let entry_arc_clone = Arc::clone(&entry_arc);
237        let locked_entry = locked_entry.deref_mut();
238
239        match locked_entry.status {
240            EntryStatus::AVAILABLE => {
241                {
242                    locked_entry.status = EntryStatus::CALCULATING;
243                    if miss_handler(&key, &mut locked_entry.data, &mut locked_entry.adhoc_code, params) {
244                        locked_entry.expiration = Instant::now() + positive_ttl;
245                        locked_entry.status = EntryStatus::READY;
246                    } else {
247                        locked_entry.expiration = Instant::now() + negative_ttl;
248                        locked_entry.status = EntryStatus::FAILED;
249                    }
250                }
251                locked_entry.cond_var.notify_all();
252            }
253            EntryStatus::CALCULATING => {
254                println!("CALCULATING");
255                match locked_entry.cond_var.wait_while(
256                    entry_arc_clone.lock().unwrap(), 
257                    |entry: &mut Entry<D>| entry.status == EntryStatus::CALCULATING)
258                    {
259                        Ok(_) => {}
260                        Err(e) => {
261                            eprintln!("Error while waiting: {:?}", e);
262                            return None;
263                        }
264                    }
265            }
266            EntryStatus::READY | EntryStatus::FAILED => {}
267        }
268        
269        
270        Some((locked_entry.data.clone(), locked_entry.adhoc_code, false))
271    }
272}
273
274// ===============================================================
275// =============================TESTS=============================
276// ===============================================================
277
278#[cfg(test)]
279mod tests {
280
281    use std::thread;
282
283    use super::*;
284    use rstest::*;
285
286    #[fixture]
287    fn simple_cache() -> Cache<i32, i32> {
288        fn miss_handler(key: &i32, data: &mut i32, adhoc_code: &mut u8, _: &[&dyn Any]) -> bool {
289            // FAIL if key is -1
290            if *key == -1 {
291                return false
292            }
293            *data = key * 2;
294            *adhoc_code += 1; // should always be 1
295            true
296        }
297        Cache::new(
298            3,
299            miss_handler,
300            Duration::from_millis(200),          
301            Duration::from_millis(100),          
302        )
303    }
304
305    #[rstest]
306    fn insert_value(simple_cache: Cache<i32, i32>) {
307        // Arrange
308        let key = 1;
309        let value = 2;
310
311        // Act
312        simple_cache.insert(&key, value);
313
314        // Assert
315        assert_eq!(simple_cache.len(), 1);
316    }
317
318    #[rstest]
319    fn insert_same_key(simple_cache: Cache<i32, i32>) {
320        // Arrange
321        let key = 1;
322        let value = 2;
323
324        // Act
325        simple_cache.insert(&key, value);
326        simple_cache.insert(&key, value);
327
328        // Assert
329        assert_eq!(simple_cache.len(), 1);
330    }
331
332    #[rstest]
333    fn get_value(simple_cache: Cache<i32, i32>) {
334        // Arrange
335        let key = 1;
336        let value = 2;
337
338        // Act
339        simple_cache.insert(&key, value);
340
341        // Assert
342        assert_eq!(simple_cache.get(&key), Some(value));
343    }
344
345    #[rstest]
346    fn get_value_not_found(simple_cache: Cache<i32, i32>) {
347        // Arrange
348        let key = 1;
349
350        // Assert
351        assert_eq!(simple_cache.get(&key), None);
352    }
353
354    #[rstest]
355    fn insert_max_capacity(simple_cache: Cache<i32, i32>) {
356        // Arrange
357        let key1 = 1;
358        let key2 = 2;
359        let key3 = 3;
360        let key4 = 4;
361        let value = 2;
362
363        // Act
364        simple_cache.insert(&key1, value);
365        simple_cache.insert(&key2, value);
366        simple_cache.insert(&key3, value);
367        simple_cache.insert(&key4, value);
368
369        // Assert
370        assert_eq!(simple_cache.len(), 3);
371        assert_eq!(simple_cache.get(&key1), None); // lru is removed
372    }
373
374    #[rstest]
375    fn get_lru_change(simple_cache: Cache<i32, i32>) {
376        // Arrange
377        let key1 = 1;
378        let key2 = 2;
379        let key3 = 3;
380        let key4 = 4;
381        let value = 2;
382
383        // Act
384        simple_cache.insert(&key1, value);
385        simple_cache.insert(&key2, value);
386        simple_cache.get(&key1); // key2 is now the lru
387        simple_cache.insert(&key3, value);
388        simple_cache.insert(&key4, value);
389
390        // Assert
391        assert_eq!(simple_cache.len(), 3);
392        assert_eq!(simple_cache.get(&key2), None); // lru is removed
393    }
394
395    #[rstest]
396    fn ttl_expired(simple_cache: Cache<i32, i32>) {
397        // Arrange
398        let key = 1;
399        let value = 2;
400
401        // Act
402        simple_cache.insert(&key, value);
403        std::thread::sleep(std::time::Duration::from_millis(250));
404
405        // Assert
406        assert_eq!(simple_cache.get(&key), None);
407    }
408
409    #[rstest]
410    fn update_value(simple_cache: Cache<i32, i32>) {
411        // Arrange
412        let key = 1;
413        let value = 2;
414
415        // Act
416        simple_cache.insert(&key, value);
417        let new_value = 3;
418        let success = simple_cache.update(&key, new_value);
419
420        // Assert
421        assert!(success);
422        assert_eq!(simple_cache.get(&key), Some(new_value));
423    }
424
425    #[rstest]
426    fn update_value_not_found(simple_cache: Cache<i32, i32>) {
427        // Arrange
428        let key = 1;
429        let new_value = 3;
430
431        // Act
432        let success = simple_cache.update(&key, new_value);
433
434        // Assert
435        assert!(!success);
436    }
437
438    #[rstest]
439    fn retrieve_or_compute_not_in_cache(simple_cache: Cache<i32, i32>){
440        // Arrange
441        let key = 1;
442
443        // Act
444        let (data, adhoc_code, is_hit) = simple_cache.retrieve_or_compute(&key).unwrap();
445
446        // Assert
447        assert_eq!(data, 2);
448        assert_eq!(adhoc_code, 1);
449        assert_eq!(is_hit, false);
450        assert_eq!(simple_cache.len(), 1);
451    }
452
453    #[rstest]
454    fn retrieve_or_compute_already_in_cache(simple_cache: Cache<i32, i32>){
455        // Arrange
456        let key = 1;
457
458        // Act
459        simple_cache.retrieve_or_compute(&key);
460        simple_cache.retrieve_or_compute(&key);
461        simple_cache.retrieve_or_compute(&key);
462        simple_cache.retrieve_or_compute(&key);
463        let (data, adhoc_code, is_hit) = simple_cache.retrieve_or_compute(&key).unwrap();
464
465        // Assert
466        assert_eq!(data, 2);
467        assert_eq!(adhoc_code, 1);
468        assert_eq!(is_hit, true);
469        assert_eq!(simple_cache.len(), 1);
470    }
471
472    #[rstest]
473    fn retrieve_or_compute_ttl_expired(simple_cache: Cache<i32, i32>){
474        // Arrange
475        let key = 1;
476        // Act
477        simple_cache.retrieve_or_compute(&key);
478        let entry_1 = simple_cache.lru_cache.lock().unwrap().peek(&key).unwrap().lock().unwrap().clone();
479        std::thread::sleep(std::time::Duration::from_millis(100));
480        simple_cache.retrieve_or_compute(&key);
481        let entry_2 = simple_cache.lru_cache.lock().unwrap().peek(&key).unwrap().lock().unwrap().clone();
482        std::thread::sleep(std::time::Duration::from_millis(150));
483        simple_cache.retrieve_or_compute(&key);
484        let entry_3 = simple_cache.lru_cache.lock().unwrap().peek(&key).unwrap().lock().unwrap().clone();
485        
486        // Assert
487        assert_eq!(entry_1.status, EntryStatus::READY);
488        assert_eq!(entry_1, entry_2); // not expired
489        assert_ne!(entry_1, entry_3); // expired 
490    }
491
492    #[rstest]
493    fn retrieve_or_compute_negative_ttl(simple_cache: Cache<i32, i32>){
494        // Arrange
495        let key = -1;
496
497        // Act
498        simple_cache.retrieve_or_compute(&key);
499        let entry_1 = simple_cache.lru_cache.lock().unwrap().peek(&key).unwrap().lock().unwrap().clone();
500        std::thread::sleep(std::time::Duration::from_millis(105));
501        simple_cache.retrieve_or_compute(&key);
502        let entry_2 = simple_cache.lru_cache.lock().unwrap().peek(&key).unwrap().lock().unwrap().clone();
503        
504        // Assert
505        assert_ne!(entry_1, entry_2); // expired because negative ttl is lower
506        assert_eq!(entry_1.status, EntryStatus::FAILED);
507    }
508
509    #[fixture]
510    fn simple_cache_with_params () -> Cache<i32, i32> {
511        fn miss_handler(key: &i32, data: &mut i32, adhoc_code: &mut u8, params: &[&dyn Any]) -> bool {
512            
513            // FAIL if key is -1
514            if *key == -1 {
515                return false
516            }
517            *data = key * 2;
518            for param in params {
519                if let Some(param) = param.downcast_ref::<i32>() {
520                    *data += param;
521                }
522            }
523            if params[0].downcast_ref::<&str>().is_some() {
524                *adhoc_code += 1;
525            }
526            *adhoc_code += 1; // should always be 1
527            true
528        }
529        Cache::new(
530            3,
531            miss_handler,
532            Duration::from_millis(200),          
533            Duration::from_millis(100),          
534        )
535    }
536
537    #[rstest]
538    fn retrieve_or_compute_with_params(simple_cache_with_params: Cache<i32, i32>){
539        // Arrange
540        let key = 1;
541        let param = 3;
542
543        // Act
544        let (data, adhoc_code, is_hit) = simple_cache_with_params.retrieve_or_compute_with_params(&key, &[&param]).unwrap();
545
546        // Assert
547        assert_eq!(data, 5);
548        assert_eq!(adhoc_code, 1);
549        assert_eq!(is_hit, false);
550        assert_eq!(simple_cache_with_params.len(), 1);
551    }
552
553    #[rstest]
554    fn retrieve_or_compute_with_multiple_params(simple_cache_with_params: Cache<i32, i32>){
555        // Arrange
556        let key = 1;
557        let param1 = 3;
558        let param2 = 4;
559
560        // Act
561        let (data, adhoc_code, is_hit) = simple_cache_with_params.retrieve_or_compute_with_params(&key, &[&param1, &param2]).unwrap();
562
563        // Assert
564        assert_eq!(data, 9);
565        assert_eq!(adhoc_code, 1);
566        assert_eq!(is_hit, false);
567        assert_eq!(simple_cache_with_params.len(), 1);
568    }
569    
570    #[rstest]
571    fn retrieve_or_compute_with_multiple_params_different_types(simple_cache_with_params: Cache<i32, i32>){
572        // Arrange
573        let key = 1;
574        let param1 = "hola";
575        let param2 = 3;
576        let param3 = 4;
577
578        // Act
579        let (data, adhoc_code, is_hit) = simple_cache_with_params.retrieve_or_compute_with_params(&key, &[&param1, &param2, &param3]).unwrap();
580
581        // Assert
582        assert_eq!(data, 9);
583        assert_eq!(adhoc_code, 2);
584        assert_eq!(is_hit, false);
585        assert_eq!(simple_cache_with_params.len(), 1);
586    }
587
588    #[fixture]
589    fn time_consuming_mh() -> Cache<i32, i32> {
590        fn miss_handler(key: &i32, data: &mut i32, adhoc_code: &mut u8, _: &[&dyn Any]) -> bool {
591
592            std::thread::sleep(std::time::Duration::from_millis(500));
593            if *key == 3 {
594                std::thread::sleep(std::time::Duration::from_millis(500));
595            }
596
597            *data = key * 2;
598            *adhoc_code += 1; // should always be 1
599            true
600        }
601        Cache::new(
602            200,
603            miss_handler,
604            Duration::from_secs(60),          
605            Duration::from_secs(60),          
606        )
607    }
608
609    #[rstest]
610    fn test_thread_safe_cache_same_key(time_consuming_mh: Cache<i32, i32>) {        
611        // Arrange            
612        let cache = Arc::new(time_consuming_mh);
613        let n_threads: i32 = 200;
614        let start = Instant::now();
615
616        // Act
617        let handles: Vec<_> = (0..n_threads).map(|i| {
618            let cache_clone = Arc::clone(&cache);
619            thread::spawn({
620                move || {
621                    let key = 456;
622                    let start = Instant::now();
623                    let res = cache_clone.retrieve_or_compute(&key);
624                    (res, start.elapsed())
625                }
626            })
627        }).collect();
628
629        // Assert
630        let results: Vec<_> = handles.into_iter().map(|handle| handle.join()).collect();
631        let duration = start.elapsed();
632        for res in results {
633            // assert res is ok
634            assert!(res.is_ok());
635            if let (Some((data, adhoc_code, _)), elapsed) = res.as_ref().unwrap() {
636                let key = 456;
637                assert_eq!(*data, key * 2);
638                assert_eq!(*adhoc_code, 1);
639                assert!(elapsed.as_millis() < 600);
640            }
641        }        
642        
643        assert!(cache.len() == 1);
644        assert!(duration.as_secs() < 1);
645        
646    }
647
648    #[rstest]
649    fn test_thread_safe_cache_different_keys(time_consuming_mh: Cache<i32, i32>) {
650        // Arrange
651        let cache = Arc::new(time_consuming_mh);
652        let n_threads = 20;
653        let start = Instant::now();
654
655        // Act
656        let handles: Vec<_> = (0..n_threads).map(|i| {
657            let cache_clone = Arc::clone(&cache);
658            thread::spawn({
659                move || {
660                    let key = 456 + i as i32;
661                    let start = Instant::now();
662                    let res = cache_clone.retrieve_or_compute(&key);
663                    (res, start.elapsed())
664                }
665            })
666        }).collect();
667let results: Vec<_> = handles.into_iter().map(|handle| handle.join()).collect();
668        
669        let duration = start.elapsed();
670        // Assert
671        for i in 0..n_threads {
672            let res = &results[i];
673            // assert res is ok
674            assert!(res.is_ok());
675            if let (Some((data, adhoc_code, _)), elapsed) = res.as_ref().unwrap() {
676                let key = 456 + i as i32;
677                assert_eq!(*data, key * 2);
678                assert_eq!(*adhoc_code, 1);
679                assert!(elapsed.as_millis() < 600);
680            }
681        }      
682        for i in 0..n_threads {
683            let key = 456 + i as i32;
684            let data = cache.get(&key);
685            assert_eq!(data, Some(key * 2));
686        }
687        assert!(cache.len() == n_threads.try_into().unwrap());
688        assert!(duration.as_secs() < 1);
689    }
690
691    #[rstest]
692    fn test_thread_safe_cache_maximum_capacity(time_consuming_mh: Cache<i32, i32>) {
693        // Arrange
694        let cache = Arc::new(time_consuming_mh);
695        let n_threads = 202;
696        let start = Instant::now();
697
698        // Act
699        let handles: Vec<_> = (0..n_threads).map(|i| {
700            let cache_clone = Arc::clone(&cache);
701            thread::spawn(move || {
702                let key = 456 + i;
703                cache_clone.retrieve_or_compute(&key);
704            })
705        }).collect();
706
707        for handle in handles {
708            let res = handle.join();
709            // assert res is ok
710            assert!(res.is_ok());
711            res.unwrap();
712        }
713
714        // Assert
715        let mut not_in_cache_count = 0;      
716        for i in 0..n_threads {
717            let key = 456 + i;
718            let data = cache.get(&key);
719            if data == None {
720                not_in_cache_count += 1;
721            } else {
722                let key = 456 + i;
723                let data = cache.get(&key);
724                assert_eq!(data, Some(key * 2));
725            }
726        }
727        let duration = start.elapsed();
728        assert!(cache.len() == 200);
729        assert!(duration.as_secs() < 1);
730        assert_eq!(not_in_cache_count, 2);
731    }
732
733    #[rstest]
734    fn test_thread_safe_heavy_threads(time_consuming_mh: Cache<i32, i32>) {
735        let cache = Arc::new(time_consuming_mh);
736        for cicle in 0..50 {
737            // Arrange
738            let n_keys = 5;
739            let entries_per_key = 20;
740            let results = vec![((0,0), Duration::default()); n_keys * entries_per_key];
741            let results_arc = Arc::new(Mutex::new(results));
742            let mut threads = Vec::<thread::JoinHandle<_>>::with_capacity(n_keys * entries_per_key);
743
744            // Act
745            for i in 0..n_keys {
746                for j in 0..entries_per_key {
747                    let cache_clone = Arc::clone(&cache);
748                    let results_clone = Arc::clone(&results_arc);
749                    threads.push(thread::spawn(move || {
750                        let key = (i + 1) as i32;
751                        let start = Instant::now();
752                        let (data, adhoc_code, _) = cache_clone.retrieve_or_compute(&key).unwrap();
753                        let mut results = results_clone.lock().unwrap();
754                        results[i*entries_per_key+j] = ((data, adhoc_code), start.elapsed());
755                    }));
756                }
757            }
758            for handle in threads {
759                let res = handle.join();
760                // assert res is ok
761                assert!(res.is_ok());
762                res.unwrap();
763            }
764
765            // Assert
766            let mut key = 0;
767            for i in (0..n_keys * entries_per_key).step_by(entries_per_key) {
768                key += 1;
769                let results = results_arc.lock().unwrap();
770                let (res_i, elapsed_i) = results[i];
771                for j in 1..entries_per_key {
772                    let (res_j, elapsed_j) = results[i+j];
773                    assert_eq!(res_i, res_j);
774                    println!("key : {} i: {} j: {}", key,  elapsed_i.as_millis(), elapsed_j.as_millis());
775                    if cicle ==0 && key == 3 {                        
776                        assert!(elapsed_i.as_millis() > 600);
777                        assert!(elapsed_j.as_millis() > 600);
778                    } else {
779                        assert!(elapsed_i.as_millis() < 600);
780                        assert!(elapsed_j.as_millis() < 600);
781                    }
782
783                }
784            }
785            assert!(cache.len() == n_keys);
786        }
787    }
788
789    #[derive(Debug, PartialEq, Eq, Clone, Hash, Copy, Default)]
790    struct SimpleStruct {
791        value: i32,
792    }
793    #[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
794    struct ComplexKey {
795        id: i32,
796        name: String,
797        nested: SimpleStruct,
798        array: Vec<i32>,
799
800    }
801
802  #[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
803    struct ComplexData {
804        value: i32,
805        description: String,
806        nested: SimpleStruct,
807        array: Vec<i32>,
808    }
809
810    #[fixture]
811    fn complex_key_and_data_cache() -> Cache<ComplexKey, ComplexData> {
812        fn miss_handler(key: &ComplexKey, data: &mut ComplexData, adhoc_code: &mut u8, _: &[&dyn Any]) -> bool {
813            // wait 500 ms
814            std::thread::sleep(std::time::Duration::from_millis(500));
815            // FAIL if key.id is -1
816            if key.id == -1 {
817                return false
818            }
819            data.value = key.id * 2;
820            data.description = key.name.clone();
821            data.nested = key.nested.clone();
822            data.array = key.array.clone();
823            *adhoc_code += 1; // should always be 1
824            true
825        }
826        Cache::new(
827            200,
828            miss_handler,
829            Duration::from_secs(1),          
830            Duration::from_secs(1),          
831        )
832    }
833
834    #[rstest]
835    fn complex_key_and_data_cache_insert(complex_key_and_data_cache: Cache<ComplexKey, ComplexData>) {
836        // Arrange
837        let key = ComplexKey {
838            id: 1,
839            name: "name".to_string(),
840            nested: SimpleStruct { value: 1 },
841            array: vec![1, 2, 3],
842        };
843        let data = ComplexData {
844            value: 2,
845            description: "name".to_string(),
846            nested: SimpleStruct { value: 1 },
847            array: vec![1, 2, 3],
848        };
849
850        // Act
851        complex_key_and_data_cache.insert(&key, data);
852
853        // Assert
854        assert_eq!(complex_key_and_data_cache.len(), 1);
855    }
856
857    #[rstest]
858    fn complex_key_data_retrieve_or_compute(complex_key_and_data_cache: Cache<ComplexKey, ComplexData>) {
859        // Arrange
860        let key = ComplexKey {
861            id: 1,
862            name: "name".to_string(),
863            nested: SimpleStruct { value: 1 },
864            array: vec![1, 2, 3],
865        };
866
867        // Act
868        let (data, adhoc_code, is_hit) = complex_key_and_data_cache.retrieve_or_compute(&key).unwrap();
869
870        // Assert
871        assert_eq!(data.value, 2);
872        assert_eq!(data.description, "name");
873        assert_eq!(data.nested, SimpleStruct { value: 1 });
874        assert_eq!(data.array, vec![1, 2, 3]);
875        assert_eq!(adhoc_code, 1);
876        assert_eq!(is_hit, false);
877        assert_eq!(complex_key_and_data_cache.len(), 1);    
878    }
879
880    #[rstest]
881    fn complex_key_data_retrieve_or_compute_change_key(complex_key_and_data_cache: Cache<ComplexKey, ComplexData>) {
882        // Arrange
883        let key = ComplexKey {
884            id: 1,
885            name: "name".to_string(),
886            nested: SimpleStruct { value: 1 },
887            array: vec![1, 2, 3],
888        };
889        let key_clone = key.clone();
890        let mut key_change = key.clone();
891        key_change.id = 2;
892
893        // Act
894        let (data, _, _) = complex_key_and_data_cache.retrieve_or_compute(&key).unwrap();
895        let (data1, _, _) = complex_key_and_data_cache.retrieve_or_compute(&key_clone).unwrap();
896        let (data2, _, _) = complex_key_and_data_cache.retrieve_or_compute(&key_change).unwrap();
897
898        // Assert
899        assert_eq!(data, data1);
900        assert_ne!(data, data2);
901        assert_eq!(complex_key_and_data_cache.len(), 2);
902    }
903
904    #[rstest]
905    fn complex_key_data_thread_safe_cache_same_key(complex_key_and_data_cache: Cache<ComplexKey, ComplexData>) {
906        // Arrange
907        let cache = Arc::new(complex_key_and_data_cache);
908        let n_threads: i32 = 200;
909        let start = Instant::now();
910        let key = ComplexKey {
911            id: 1,
912            name: "name".to_string(),
913            nested: SimpleStruct { value: 1 },
914            array: vec![1, 2, 3],
915        };
916
917        // Act
918        let handles: Vec<_> = (0..n_threads).map(|i| {
919            let cache_clone = Arc::clone(&cache);
920            thread::Builder::new().name(format!("Thread {}", i)).spawn({
921            let value = key.clone();
922            move || {
923            cache_clone.retrieve_or_compute(&value)
924            }
925            })
926        }).collect();
927
928        // Assert
929        let results = handles.into_iter().map(|handle| handle.unwrap().join());
930        let mut i = 0;
931        for res in results {
932            // assert res is ok
933            assert!(res.is_ok());
934            if let Some((data, adhoc_code, is_hit)) = res.unwrap() {
935                assert_eq!(data.value, 2);
936                assert_eq!(data.description, "name");
937                assert_eq!(data.nested, SimpleStruct { value: 1 });
938                assert_eq!(data.array, vec![1, 2, 3]);
939                assert_eq!(adhoc_code, 1);
940                if i == 0 {
941                     assert!(!is_hit);
942                }else {
943                    assert!(is_hit);
944                }
945            }
946            i += 1;
947        }        
948        let duration = start.elapsed();
949        assert!(cache.len() == 1);
950        assert!(duration.as_secs() < 1);
951        
952    }
953
954    #[rstest]
955    fn complex_key_data_thread_safe_cache_different_keys(complex_key_and_data_cache: Cache<ComplexKey, ComplexData>) {
956        // Arrange
957        let cache = Arc::new(complex_key_and_data_cache);
958        let n_threads = 200;
959        let start = Instant::now();
960
961        // Act
962        let handles: Vec<_> = (0..n_threads).map(|i| {
963            let cache_clone = Arc::clone(&cache);
964            let key: ComplexKey = ComplexKey {
965                id: 1 + i,
966                name: "name".to_string(),
967                nested: SimpleStruct { value: 1 },
968                array: vec![1, 2, 3],
969            };
970            thread::spawn({ move || {
971            cache_clone.retrieve_or_compute(&key)
972            }
973            })
974        }).collect();
975
976        for handle in handles {
977            let res = handle.join();
978            // assert res is ok
979            assert!(res.is_ok());
980            res.unwrap();
981        }
982
983        // Assert        
984        for i in 0..n_threads {
985            let key = ComplexKey {
986                id: 1 + i,
987                name: "name".to_string(),
988                nested: SimpleStruct { value: 1 },
989                array: vec![1, 2, 3],
990            };
991            let data = cache.get(&key);
992
993            assert_eq!(data, Some(ComplexData {
994                value: (1 + i) * 2,
995                description: "name".to_string(),
996                nested: SimpleStruct { value: 1 },
997                array: vec![1, 2, 3],
998            }));
999        }
1000        assert!(cache.len() == n_threads.try_into().unwrap());
1001        let duration = start.elapsed();
1002        assert!(duration.as_secs() < 1);
1003    }
1004
1005
1006}