Skip to main content

pdk_cache_lib/
fifo_cache.rs

1// Copyright 2023 Salesforce, Inc. All rights reserved.
2use log;
3use serde::{Deserialize, Serialize};
4use std::collections::VecDeque;
5
6use super::{error::CacheError, Cache};
7use pdk_core::classy::shared_data::concurrent_shared_data::TransactionStatus;
8
9cfg_if::cfg_if! {
10    if #[cfg(test)] {
11        use crate::mocks::MockConcurrentSharedData as ConcurrentSharedData;
12    } else {
13        use pdk_core::classy::shared_data::concurrent_shared_data::ConcurrentSharedData;
14    }
15}
16
17enum OperationType {
18    Insert,
19    Remove,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23struct InternalData {
24    max_entries_size: usize,
25    fifo: VecDeque<String>,
26    last_modified_item_key: String,
27}
28
29impl InternalData {
30    fn new(max_entries_size: usize, fifo: VecDeque<String>, last_modified_item_key: &str) -> Self {
31        Self {
32            max_entries_size,
33            fifo,
34            last_modified_item_key: last_modified_item_key.to_string(),
35        }
36    }
37}
38
39/// A FIFO cache, that shares global state along all incoming requests of a policy.
40///
41/// As the name suggests, a FIFO eviction strategy is used, meaning that when the maximum number
42/// of entries is reached, the first added cache entries will be the first to be evicted.
43pub struct FifoCache {
44    cache_id: String,
45    cache_id_internal_data: String,
46    concurrent_shared_data: ConcurrentSharedData,
47    max_entries_size: usize,
48}
49
50impl FifoCache {
51    pub(crate) fn new(
52        cache_id: String,
53        max_entries_size: usize,
54        concurrent_shared_data: ConcurrentSharedData,
55    ) -> Self {
56        Self {
57            cache_id_internal_data: format!("{}-internal_data", &cache_id),
58            cache_id,
59            max_entries_size,
60            concurrent_shared_data,
61        }
62    }
63
64    /// Get the cache's maximum number of entries available.
65    pub fn max_entries_size(&self) -> usize {
66        self.max_entries_size
67    }
68
69    /// Returns cache ID.
70    pub fn cache_id(&self) -> &str {
71        self.cache_id.as_str()
72    }
73
74    /// Get a reference to the fifo shared data cache's cache id internal data.
75    fn cache_id_internal_data(&self) -> &str {
76        self.cache_id_internal_data.as_ref()
77    }
78
79    /// Creates a key to be used by cache to store data, using cache instance ID as prefix.
80    fn cache_key(&self, item_key: &str) -> String {
81        format!("{}-{}", self.cache_id(), item_key)
82    }
83
84    fn new_internal_data(
85        &self,
86        fifo: VecDeque<String>,
87        last_modified_item_key: &str,
88    ) -> InternalData {
89        InternalData::new(self.max_entries_size(), fifo, last_modified_item_key)
90    }
91
92    fn get_stored_internal_data(&self) -> Option<InternalData> {
93        self.concurrent_shared_data
94            .get::<InternalData>(self.cache_id_internal_data())
95    }
96
97    fn update_internal_data(&self, last_in: String, is_update: bool) {
98        log::trace!(
99            "Updating cache state data for key: {} (updated: {})",
100            &last_in,
101            is_update
102        );
103
104        if is_update {
105            self.remove_item_from_internal_data(&last_in);
106        }
107
108        // update variable with shared_data info; otherwise init as empty.
109        let mut internal_data: InternalData;
110        if let Some(stored_internal_data) = self.get_stored_internal_data() {
111            internal_data = stored_internal_data;
112        } else {
113            internal_data =
114                self.new_internal_data(VecDeque::with_capacity(self.max_entries_size()), &last_in);
115        }
116
117        // insert in queue
118        internal_data.fifo.push_back(last_in.clone());
119        internal_data.last_modified_item_key = last_in;
120
121        // Save updated internal data
122        self.save_internal_data(&internal_data, OperationType::Insert);
123
124        // check eviction
125        self.check_eviction(&mut internal_data);
126    }
127
128    fn check_eviction(&self, internal_data: &mut InternalData) {
129        let mut is_evicted = false;
130        while internal_data.fifo.len() > self.max_entries_size() {
131            let first_in = internal_data.fifo.pop_front().unwrap();
132
133            self.evict(&first_in);
134            internal_data.last_modified_item_key = first_in;
135            is_evicted = true;
136        }
137
138        if is_evicted {
139            self.save_internal_data(internal_data, OperationType::Remove);
140            log::trace!("Cache evicted");
141        }
142    }
143
144    fn evict(&self, key: &str) {
145        let _deleted: Option<Vec<u8>> = self.concurrent_shared_data.remove(key);
146    }
147
148    fn save_internal_data(&self, new_internal_data: &InternalData, operation_type: OperationType) {
149        // declare unique variable since destructuring assignments are unstable
150        // see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
151        // related issue: https://github.com/rust-lang/rust/issues/90095
152        let insert_result: (TransactionStatus, InternalData) = match operation_type {
153            OperationType::Insert => self.concurrent_shared_data.insert(
154                self.cache_id_internal_data().to_string(),
155                new_internal_data.to_owned(),
156                handle_cache_state_consistency_when_insert,
157            ),
158            OperationType::Remove => self.concurrent_shared_data.insert(
159                self.cache_id_internal_data().to_string(),
160                new_internal_data.to_owned(),
161                handle_cache_state_consistency_when_remove_or_evict,
162            ),
163        };
164
165        if !matches!(insert_result.0, TransactionStatus::Complete) {
166            log::error!(
167                "Error updating internal cache state {}: {:?}",
168                self.cache_id(),
169                insert_result.0
170            );
171        }
172    }
173
174    fn remove_item_from_internal_data(&self, deleted_key: &str) {
175        if let Some(mut stored_internal_data) = self.get_stored_internal_data() {
176            // Remove from queue
177            if let Some(found_index) =
178                stored_internal_data
179                    .fifo
180                    .iter()
181                    .enumerate()
182                    .find_map(|(index, key)| {
183                        if key.as_str() == deleted_key {
184                            Some(index)
185                        } else {
186                            None
187                        }
188                    })
189            {
190                stored_internal_data.fifo.remove(found_index);
191                stored_internal_data.last_modified_item_key = deleted_key.to_string();
192
193                // Store updated value
194                self.save_internal_data(&stored_internal_data, OperationType::Remove);
195            }
196        } else {
197            log::trace!("Stored cache state data not found, nothing to clean from cache state");
198        }
199    }
200}
201
202/// In any case, if prev_value was already inserted or not, deleted or not; we decide override existing value with new one.
203fn handle_cache_value_consistency(
204    _stored_value: Vec<u8>,
205    candidate_value: Vec<u8>,
206) -> Option<Vec<u8>> {
207    Some(candidate_value)
208}
209
210/// This method applies for insertion operations, and adds last inserted into stored state.
211fn handle_cache_state_consistency_when_insert(
212    stored_state: InternalData,
213    candidate_state: InternalData,
214) -> Option<InternalData> {
215    let mut new_fifo: VecDeque<String> = stored_state.fifo;
216
217    if let Some(index) = new_fifo.iter().enumerate().find_map(|(index, key)| {
218        if key.as_str() == candidate_state.last_modified_item_key.as_str() {
219            Some(index)
220        } else {
221            None
222        }
223    }) {
224        new_fifo.remove(index);
225    }
226    new_fifo.push_back(candidate_state.last_modified_item_key.clone());
227
228    Some(InternalData::new(
229        stored_state.max_entries_size,
230        new_fifo,
231        candidate_state.last_modified_item_key.as_str(),
232    ))
233}
234
235/// This method applies for removing operations, and removes all elements not present in candidate state from stored state.
236fn handle_cache_state_consistency_when_remove_or_evict(
237    stored_state: InternalData,
238    candidate_state: InternalData,
239) -> Option<InternalData> {
240    let mut new_fifo: VecDeque<String> = stored_state.fifo;
241    new_fifo.retain(|key| key.as_str() != candidate_state.last_modified_item_key.as_str());
242
243    Some(InternalData::new(
244        stored_state.max_entries_size,
245        new_fifo,
246        candidate_state.last_modified_item_key.as_str(),
247    ))
248}
249
250impl Cache for FifoCache {
251    fn save(&self, key: &str, value: Vec<u8>) -> Result<(), CacheError> {
252        // store data in shared data
253        let cache_key = self.cache_key(key);
254
255        // First, verify if key is already stored in cache,
256        // in order to see if we need to remove element from FIFO and then update cache item correctly.
257        let is_update = self
258            .concurrent_shared_data
259            .get::<Vec<u8>>(cache_key.as_str())
260            .is_some();
261
262        // After cleaning up FIFO, let's udpate cache item.
263        let (tx_status, _saved_data) =
264            self.concurrent_shared_data
265                .insert(cache_key, value, handle_cache_value_consistency);
266
267        match tx_status {
268            TransactionStatus::Complete => {
269                self.update_internal_data(self.cache_key(key), is_update);
270            }
271            _ => {
272                return Err(CacheError::SaveError {
273                    status: format!("{:?}", tx_status),
274                    cache_id: String::from(self.cache_id()),
275                })
276            }
277        }
278        Ok(())
279    }
280
281    fn get(&self, key: &str) -> Option<Vec<u8>> {
282        let cache_key = self.cache_key(key);
283        self.concurrent_shared_data
284            .get::<Vec<u8>>(cache_key.as_str())
285    }
286
287    fn delete(&self, key: &str) -> Option<Vec<u8>> {
288        log::trace!("Delete item '{}' from cache", key);
289        let delete_key = self.cache_key(key);
290        let deleted = self.concurrent_shared_data.remove(&delete_key);
291        self.remove_item_from_internal_data(&delete_key);
292        deleted
293    }
294
295    fn purge(&self) {
296        if let Some(cache_internal_data) = self.get_stored_internal_data() {
297            // Clear cache items
298            cache_internal_data.fifo.iter().for_each(|cache_key| {
299                let _removed: Option<Vec<u8>> = self.concurrent_shared_data.remove(cache_key);
300            });
301            // Clear internal data
302            let _internal_data: Option<InternalData> = self
303                .concurrent_shared_data
304                .remove(self.cache_id_internal_data());
305        }
306    }
307}
308
309#[cfg(test)]
310mod tests {
311    use std::collections::VecDeque;
312
313    use mockall::{
314        predicate::{always, eq},
315        Sequence,
316    };
317    use serde::{Deserialize, Serialize};
318
319    use super::Cache;
320    use super::ConcurrentSharedData as MockConcurrentSharedData;
321    use super::{
322        handle_cache_state_consistency_when_insert,
323        handle_cache_state_consistency_when_remove_or_evict, handle_cache_value_consistency,
324        FifoCache, InternalData,
325    };
326    use pdk_core::classy::shared_data::concurrent_shared_data::TransactionStatus;
327
328    #[derive(Debug, Serialize, Deserialize, PartialEq, Copy, Clone)]
329    struct SerializableObject {
330        property_one: u64,
331        property_two: u64,
332    }
333
334    #[test]
335    fn save_in_cache_first_time() {
336        let obj = SerializableObject {
337            property_one: 1,
338            property_two: 10,
339        };
340        let serialized_obj = bincode::serialize(&obj).unwrap();
341
342        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
343        let mut sequence = Sequence::new();
344
345        mock_concurrent_shared_data
346            .expect_get::<Vec<u8>>()
347            .with(eq("test-cache-key1"))
348            .times(1)
349            .in_sequence(&mut sequence)
350            .returning(move |_x: &str| None);
351
352        mock_concurrent_shared_data
353            .expect_insert()
354            .times(1)
355            .in_sequence(&mut sequence)
356            .returning(move |_x, value: Vec<u8>, _handle_consistency| {
357                (TransactionStatus::Complete, value)
358            });
359
360        mock_concurrent_shared_data
361            .expect_get::<InternalData>()
362            .with(eq("test-cache-internal_data"))
363            .times(1)
364            .in_sequence(&mut sequence)
365            .returning(move |_x: &str| None);
366
367        mock_concurrent_shared_data
368            .expect_insert()
369            .times(1)
370            .in_sequence(&mut sequence)
371            .returning(move |_x, value: InternalData, _handle_consistency| {
372                (TransactionStatus::Complete, value)
373            });
374
375        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
376        let value = serialized_obj.as_slice().to_vec();
377        assert!(cache.save("key1", value).is_ok());
378    }
379
380    #[test]
381    fn save_in_cache_already_exists() {
382        let obj = SerializableObject {
383            property_one: 1,
384            property_two: 10,
385        };
386        let serialized_obj = bincode::serialize(&obj).unwrap();
387
388        let stored_obj = SerializableObject {
389            property_one: 1,
390            property_two: 10,
391        };
392        let serialized_stored_obj = bincode::serialize(&stored_obj).unwrap();
393
394        let stored_fifo = VecDeque::from([String::from("test-cache-key1")]);
395        let stored_internal_data = InternalData::new(3, stored_fifo, "test-cache-key1");
396
397        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
398        let mut sequence = Sequence::new();
399
400        mock_concurrent_shared_data
401            .expect_get::<Vec<u8>>()
402            .with(eq("test-cache-key1"))
403            .times(1)
404            .in_sequence(&mut sequence)
405            .returning(move |_x: &str| Some(serialized_stored_obj.clone()));
406
407        mock_concurrent_shared_data
408            .expect_insert()
409            .times(1)
410            .in_sequence(&mut sequence)
411            .returning(move |_x, value: Vec<u8>, _handle_consistency| {
412                (TransactionStatus::Complete, value)
413            });
414
415        mock_concurrent_shared_data
416            .expect_get::<InternalData>()
417            .with(eq("test-cache-internal_data"))
418            .times(1)
419            .in_sequence(&mut sequence)
420            .returning(move |_x: &str| Some(stored_internal_data.clone()));
421
422        mock_concurrent_shared_data
423            .expect_insert()
424            .times(1)
425            .in_sequence(&mut sequence)
426            .returning(move |_x, value: InternalData, _handle_consistency| {
427                (TransactionStatus::Complete, value)
428            });
429
430        let stored_internal_data = InternalData::new(3, VecDeque::new(), "test-cache-key1");
431
432        mock_concurrent_shared_data
433            .expect_get::<InternalData>()
434            .with(eq("test-cache-internal_data"))
435            .times(1)
436            .in_sequence(&mut sequence)
437            .returning(move |_x: &str| Some(stored_internal_data.clone()));
438
439        mock_concurrent_shared_data
440            .expect_insert()
441            .times(1)
442            .in_sequence(&mut sequence)
443            .returning(move |_x, value: InternalData, _handle_consistency| {
444                (TransactionStatus::Complete, value)
445            });
446
447        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
448        let value = serialized_obj.as_slice().to_vec();
449        assert!(cache.save("key1", value).is_ok());
450    }
451
452    #[test]
453    fn save_in_cache_when_shared_data_fails_storing_value() {
454        let obj = SerializableObject {
455            property_one: 1,
456            property_two: 10,
457        };
458        let serialized_obj = bincode::serialize(&obj).unwrap();
459
460        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
461        let mut sequence = Sequence::new();
462
463        mock_concurrent_shared_data
464            .expect_get::<Vec<u8>>()
465            .with(eq("test-cache-key1"))
466            .times(1)
467            .in_sequence(&mut sequence)
468            .returning(move |_x: &str| None);
469
470        mock_concurrent_shared_data
471            .expect_insert()
472            .times(1)
473            .in_sequence(&mut sequence)
474            .returning(move |_x, value: Vec<u8>, _handle_consistency| {
475                (TransactionStatus::InternalError, value)
476            });
477
478        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
479        let value = serialized_obj.as_slice().to_vec();
480        assert!(cache.save("key1", value).is_err());
481    }
482
483    // TODO: W-10712428 Update test when implement rollback for errors when save internal data.
484    #[test]
485    fn save_in_cache_when_shared_data_fails_storing_internaldata() {
486        let obj = SerializableObject {
487            property_one: 1,
488            property_two: 10,
489        };
490        let serialized_obj = bincode::serialize(&obj).unwrap();
491
492        let stored_obj = SerializableObject {
493            property_one: 1,
494            property_two: 10,
495        };
496        let serialized_stored_obj = bincode::serialize(&stored_obj).unwrap();
497
498        let stored_fifo = VecDeque::from([String::from("test-cache-key1")]);
499        let stored_internal_data = InternalData::new(3, stored_fifo, "test-cache-key1");
500
501        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
502        let mut sequence = Sequence::new();
503
504        mock_concurrent_shared_data
505            .expect_get::<Vec<u8>>()
506            .with(eq("test-cache-key1"))
507            .times(1)
508            .in_sequence(&mut sequence)
509            .returning(move |_x: &str| Some(serialized_stored_obj.clone()));
510
511        mock_concurrent_shared_data
512            .expect_insert()
513            .times(1)
514            .in_sequence(&mut sequence)
515            .returning(move |_x, value: Vec<u8>, _handle_consistency| {
516                (TransactionStatus::Complete, value)
517            });
518
519        mock_concurrent_shared_data
520            .expect_get::<InternalData>()
521            .with(eq("test-cache-internal_data"))
522            .times(1)
523            .in_sequence(&mut sequence)
524            .returning(move |_x: &str| Some(stored_internal_data.clone()));
525
526        mock_concurrent_shared_data
527            .expect_insert()
528            .times(1)
529            .in_sequence(&mut sequence)
530            .returning(move |_x, value: InternalData, _handle_consistency| {
531                (TransactionStatus::InternalError, value)
532            });
533
534        let stored_internal_data = InternalData::new(3, VecDeque::new(), "test-cache-key1");
535
536        mock_concurrent_shared_data
537            .expect_get::<InternalData>()
538            .with(eq("test-cache-internal_data"))
539            .times(1)
540            .in_sequence(&mut sequence)
541            .returning(move |_x: &str| Some(stored_internal_data.clone()));
542
543        mock_concurrent_shared_data
544            .expect_insert()
545            .times(1)
546            .in_sequence(&mut sequence)
547            .returning(move |_x, value: InternalData, _handle_consistency| {
548                (TransactionStatus::Complete, value)
549            });
550
551        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
552        let value = serialized_obj.as_slice().to_vec();
553        assert!(cache.save("key1", value).is_ok());
554    }
555
556    #[test]
557    fn save_in_cache_first_time_when_cache_has_elements() {
558        let obj = SerializableObject {
559            property_one: 1,
560            property_two: 10,
561        };
562        let serialized_obj = bincode::serialize(&obj).unwrap();
563
564        let stored_fifo = VecDeque::from([
565            String::from("test-cache-keyA"),
566            String::from("test-cache-keyB"),
567        ]);
568        let stored_internal_data = InternalData::new(3, stored_fifo, "test-cache-keyB");
569
570        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
571        let mut sequence = Sequence::new();
572
573        mock_concurrent_shared_data
574            .expect_get::<Vec<u8>>()
575            .with(eq("test-cache-key1"))
576            .times(1)
577            .in_sequence(&mut sequence)
578            .returning(move |_x: &str| None);
579
580        mock_concurrent_shared_data
581            .expect_insert()
582            .times(1)
583            .with(eq(String::from("test-cache-key1")), always(), always())
584            .in_sequence(&mut sequence)
585            .returning(move |_x, value: Vec<u8>, _handle_consistency| {
586                (TransactionStatus::Complete, value)
587            });
588
589        mock_concurrent_shared_data
590            .expect_get::<InternalData>()
591            .with(eq("test-cache-internal_data"))
592            .times(1)
593            .in_sequence(&mut sequence)
594            .returning(move |_x: &str| Some(stored_internal_data.clone()));
595
596        mock_concurrent_shared_data
597            .expect_insert()
598            .times(1)
599            .in_sequence(&mut sequence)
600            .returning(move |_x, value: InternalData, _handle_consistency| {
601                (TransactionStatus::InternalError, value)
602            });
603
604        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
605        let value = serialized_obj.as_slice().to_vec();
606        assert!(cache.save("key1", value).is_ok());
607    }
608
609    #[test]
610    fn save_in_cache_when_cache_is_full_needs_eviction() {
611        let obj = SerializableObject {
612            property_one: 1,
613            property_two: 10,
614        };
615        let serialized_obj = bincode::serialize(&obj).unwrap();
616
617        let stored_fifo = VecDeque::from([
618            String::from("test-cache-keyA"),
619            String::from("test-cache-keyB"),
620            String::from("test-cache-keyC"),
621        ]);
622        let stored_internal_data = InternalData::new(3, stored_fifo, "test-cache-keyC");
623
624        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
625        let mut sequence = Sequence::new();
626
627        mock_concurrent_shared_data
628            .expect_get::<Vec<u8>>()
629            .with(eq("test-cache-key1"))
630            .times(1)
631            .in_sequence(&mut sequence)
632            .returning(move |_x: &str| None);
633
634        mock_concurrent_shared_data
635            .expect_insert()
636            .times(1)
637            .with(eq("test-cache-key1".to_string()), always(), always())
638            .in_sequence(&mut sequence)
639            .returning(move |_x, value: Vec<u8>, _handle_consistency| {
640                (TransactionStatus::Complete, value)
641            });
642
643        mock_concurrent_shared_data
644            .expect_get::<InternalData>()
645            .with(eq("test-cache-internal_data"))
646            .times(1)
647            .in_sequence(&mut sequence)
648            .returning(move |_x: &str| Some(stored_internal_data.clone()));
649
650        mock_concurrent_shared_data
651            .expect_insert()
652            .times(1)
653            .with(
654                eq(String::from("test-cache-internal_data")),
655                always(),
656                always(),
657            )
658            .in_sequence(&mut sequence)
659            .returning(move |_x, value: InternalData, _handle_consistency| {
660                (TransactionStatus::Complete, value)
661            });
662
663        // eviction value
664        mock_concurrent_shared_data
665            .expect_remove::<Vec<u8>>()
666            .times(1)
667            .with(eq("test-cache-keyA"))
668            .in_sequence(&mut sequence)
669            .returning(move |_key_to_evict| None);
670
671        let stored_fifo = VecDeque::from([
672            String::from("test-cache-keyA"),
673            String::from("test-cache-keyB"),
674            String::from("test-cache-keyC"),
675            String::from("test-cache-key1"),
676        ]);
677        let _stored_internal_data = InternalData::new(3, stored_fifo, "test-cache-key1");
678
679        // update after eviction
680        mock_concurrent_shared_data
681            .expect_insert()
682            .times(1)
683            .with(
684                eq(String::from("test-cache-internal_data")),
685                always(),
686                always(),
687            )
688            .in_sequence(&mut sequence)
689            .returning(move |_x, value: InternalData, _handle_consistency| {
690                (TransactionStatus::Complete, value)
691            });
692
693        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
694        let value = serialized_obj.as_slice().to_vec();
695        assert!(cache.save("key1", value).is_ok());
696    }
697
698    #[test]
699    fn save_in_cache_when_cache_item_has_version_mismatch() {
700        let obj = SerializableObject {
701            property_one: 1,
702            property_two: 10,
703        };
704        let serialized_obj = bincode::serialize(&obj).unwrap();
705
706        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
707        let mut sequence = Sequence::new();
708
709        mock_concurrent_shared_data
710            .expect_get::<Vec<u8>>()
711            .with(eq("test-cache-key1"))
712            .times(1)
713            .in_sequence(&mut sequence)
714            .returning(move |_x: &str| None);
715
716        mock_concurrent_shared_data
717            .expect_insert()
718            .times(1)
719            .in_sequence(&mut sequence)
720            .returning(move |_x, value: Vec<u8>, _handle_consistency| {
721                (TransactionStatus::Rejected, value)
722            });
723
724        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
725        let value = serialized_obj.as_slice().to_vec();
726        assert!(cache.save("key1", value).is_err());
727    }
728
729    #[test]
730    fn test_handle_cache_value_consistency() {
731        let vec1 = "item1".as_bytes().to_vec();
732        let vec2 = "item2".as_bytes().to_vec();
733        assert_eq!(
734            handle_cache_value_consistency(vec1, vec2.clone()).unwrap(),
735            vec2
736        );
737    }
738
739    #[test]
740    fn test_handle_cache_value_consistency_when_remove_or_evict() {
741        let vec1 = "item1".as_bytes().to_vec();
742        let vec2 = "".as_bytes().to_vec();
743        assert_eq!(
744            handle_cache_value_consistency(vec1, vec2.clone()).unwrap(),
745            vec2
746        );
747    }
748
749    #[test]
750    fn test_handle_cache_state_consistency_when_insert() {
751        let fifo1 = VecDeque::from(["A".to_string(), "B".to_string(), "C".to_string()]);
752        let state1: InternalData = InternalData::new(3, fifo1, "C");
753
754        let fifo2 = VecDeque::from(["A".to_string(), "B".to_string(), "D".to_string()]);
755        let state2: InternalData = InternalData::new(3, fifo2, "D");
756
757        let merged_state = handle_cache_state_consistency_when_insert(state1, state2);
758        assert!(merged_state.is_some());
759
760        let merged_internal_data = merged_state.unwrap();
761        assert_eq!(merged_internal_data.fifo.len(), 4);
762        assert!(merged_internal_data.fifo.contains(&"A".to_string()));
763        assert!(merged_internal_data.fifo.contains(&"B".to_string()));
764        assert!(merged_internal_data.fifo.contains(&"C".to_string()));
765        assert_eq!(merged_internal_data.fifo.back().unwrap(), &"D".to_string());
766        assert_eq!(merged_internal_data.last_modified_item_key, "D".to_string());
767    }
768
769    #[test]
770    fn test_handle_cache_state_consistency_when_insert_with_repeated_keys() {
771        let fifo1 = VecDeque::from(["A".to_string(), "B".to_string(), "D".to_string()]);
772        let state1: InternalData = InternalData::new(3, fifo1, "D");
773
774        let fifo2 = VecDeque::from(["A".to_string(), "B".to_string(), "D".to_string()]);
775        let state2: InternalData = InternalData::new(3, fifo2, "D");
776
777        let merged_state = handle_cache_state_consistency_when_insert(state1, state2);
778        assert!(merged_state.is_some());
779
780        let merged_internal_data = merged_state.unwrap();
781        assert_eq!(merged_internal_data.fifo.len(), 3);
782        assert!(merged_internal_data.fifo.contains(&"A".to_string()));
783        assert!(merged_internal_data.fifo.contains(&"B".to_string()));
784        assert_eq!(merged_internal_data.fifo.back().unwrap(), &"D".to_string());
785        assert_eq!(merged_internal_data.last_modified_item_key, "D".to_string());
786    }
787
788    #[test]
789    fn test_handle_cache_state_consistency_when_insert_with_initial_less_keys() {
790        let fifo1 = VecDeque::from(["A".to_string(), "C".to_string()]);
791        let state1: InternalData = InternalData::new(3, fifo1, "C");
792
793        let fifo2 = VecDeque::from(["A".to_string(), "B".to_string(), "D".to_string()]);
794        let state2: InternalData = InternalData::new(3, fifo2, "D");
795
796        let merged_state = handle_cache_state_consistency_when_insert(state1, state2);
797        assert!(merged_state.is_some());
798
799        let merged_internal_data = merged_state.unwrap();
800        assert_eq!(merged_internal_data.fifo.len(), 3);
801        assert!(merged_internal_data.fifo.contains(&"A".to_string()));
802        assert!(merged_internal_data.fifo.contains(&"C".to_string()));
803        assert_eq!(merged_internal_data.fifo.back().unwrap(), &"D".to_string());
804        assert_eq!(merged_internal_data.last_modified_item_key, "D".to_string());
805    }
806
807    #[test]
808    fn test_handle_cache_state_consistency_when_insert_with_initial_more_keys() {
809        let fifo1 = VecDeque::from([
810            "A".to_string(),
811            "C".to_string(),
812            "D".to_string(),
813            "E".to_string(),
814        ]);
815        let state1: InternalData = InternalData::new(3, fifo1, "C");
816
817        let fifo2 = VecDeque::from(["A".to_string(), "B".to_string(), "F".to_string()]);
818        let state2: InternalData = InternalData::new(3, fifo2, "F");
819
820        let merged_state = handle_cache_state_consistency_when_insert(state1, state2);
821        assert!(merged_state.is_some());
822
823        let merged_internal_data = merged_state.unwrap();
824        assert_eq!(merged_internal_data.fifo.len(), 5);
825        assert!(merged_internal_data.fifo.contains(&"A".to_string()));
826        assert!(merged_internal_data.fifo.contains(&"C".to_string()));
827        assert!(merged_internal_data.fifo.contains(&"D".to_string()));
828        assert!(merged_internal_data.fifo.contains(&"E".to_string()));
829        assert_eq!(merged_internal_data.fifo.back().unwrap(), &"F".to_string());
830        assert_eq!(merged_internal_data.last_modified_item_key, "F".to_string());
831    }
832
833    #[test]
834    fn test_handle_cache_state_consistency_when_insert_with_initial_more_keys_and_repeated() {
835        let fifo1 = VecDeque::from([
836            "A".to_string(),
837            "C".to_string(),
838            "D".to_string(),
839            "E".to_string(),
840        ]);
841        let state1: InternalData = InternalData::new(3, fifo1, "C");
842
843        let fifo2 = VecDeque::from(["A".to_string(), "B".to_string(), "D".to_string()]);
844        let state2: InternalData = InternalData::new(3, fifo2, "D");
845
846        let merged_state = handle_cache_state_consistency_when_insert(state1, state2);
847        assert!(merged_state.is_some());
848
849        let merged_internal_data = merged_state.unwrap();
850        assert_eq!(merged_internal_data.fifo.len(), 4);
851        assert!(merged_internal_data.fifo.contains(&"A".to_string()));
852        assert!(merged_internal_data.fifo.contains(&"C".to_string()));
853        assert!(merged_internal_data.fifo.contains(&"E".to_string()));
854        assert_eq!(merged_internal_data.fifo.back().unwrap(), &"D".to_string());
855        assert_eq!(merged_internal_data.last_modified_item_key, "D".to_string());
856    }
857
858    #[test]
859    fn test_handle_cache_state_consistency_when_remove_or_evict() {
860        let fifo1 = VecDeque::from(["A".to_string(), "B".to_string(), "C".to_string()]);
861        let state1: InternalData = InternalData::new(3, fifo1, "C");
862
863        let fifo2 = VecDeque::from(["A".to_string(), "C".to_string()]);
864        let state2: InternalData = InternalData::new(3, fifo2, "B");
865
866        let merged_state = handle_cache_state_consistency_when_remove_or_evict(state1, state2);
867        assert!(merged_state.is_some());
868
869        let merged_internal_data = merged_state.unwrap();
870        assert_eq!(merged_internal_data.fifo.len(), 2);
871        assert!(merged_internal_data.fifo.contains(&"A".to_string()));
872        assert!(merged_internal_data.fifo.contains(&"C".to_string()));
873        assert_eq!(merged_internal_data.last_modified_item_key, "B".to_string());
874    }
875
876    #[test]
877    fn test_handle_cache_state_consistency_when_remove_or_evict_with_prev_insert() {
878        let fifo1 = VecDeque::from([
879            "A".to_string(),
880            "B".to_string(),
881            "C".to_string(),
882            "D".to_string(),
883        ]);
884        let state1: InternalData = InternalData::new(3, fifo1, "C");
885
886        let fifo2 = VecDeque::from(["A".to_string(), "C".to_string()]);
887        let state2: InternalData = InternalData::new(3, fifo2, "B");
888
889        let merged_state = handle_cache_state_consistency_when_remove_or_evict(state1, state2);
890        assert!(merged_state.is_some());
891
892        let merged_internal_data = merged_state.unwrap();
893        assert_eq!(merged_internal_data.fifo.len(), 3);
894        assert!(merged_internal_data.fifo.contains(&"A".to_string()));
895        assert!(merged_internal_data.fifo.contains(&"C".to_string()));
896        assert!(merged_internal_data.fifo.contains(&"D".to_string()));
897        assert_eq!(merged_internal_data.last_modified_item_key, "B".to_string());
898    }
899
900    #[test]
901    fn remove_from_cache_when_item_exists() {
902        let stored_fifo = VecDeque::from([
903            String::from("test-cache-keyA"),
904            String::from("test-cache-key1"),
905        ]);
906        let stored_internal_data = InternalData::new(3, stored_fifo, "test-cache-key1");
907
908        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
909        let mut sequence = Sequence::new();
910
911        mock_concurrent_shared_data
912            .expect_remove()
913            .times(1)
914            .with(eq("test-cache-key1"))
915            .in_sequence(&mut sequence)
916            .returning(move |_key| Some(Vec::<u8>::new()));
917
918        mock_concurrent_shared_data
919            .expect_get::<InternalData>()
920            .with(eq("test-cache-internal_data"))
921            .times(1)
922            .in_sequence(&mut sequence)
923            .returning(move |_x: &str| Some(stored_internal_data.clone()));
924
925        mock_concurrent_shared_data
926            .expect_insert()
927            .times(1)
928            .in_sequence(&mut sequence)
929            .returning(move |_x, value: InternalData, _handle_consistency| {
930                (TransactionStatus::Complete, value)
931            });
932
933        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
934        cache.delete("key1");
935    }
936
937    #[test]
938    fn remove_from_cache_when_item_is_deleted() {
939        let stored_fifo = VecDeque::from([
940            String::from("test-cache-keyA"),
941            String::from("test-cache-keyB"),
942        ]);
943        let stored_internal_data = InternalData::new(3, stored_fifo, "test-cache-keyB");
944
945        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
946        let mut sequence = Sequence::new();
947
948        mock_concurrent_shared_data
949            .expect_remove::<Vec<u8>>()
950            .times(1)
951            .with(eq("test-cache-key1"))
952            .in_sequence(&mut sequence)
953            .returning(move |_key| None);
954
955        mock_concurrent_shared_data
956            .expect_get::<InternalData>()
957            .with(eq("test-cache-internal_data"))
958            .times(1)
959            .in_sequence(&mut sequence)
960            .returning(move |_x: &str| Some(stored_internal_data.clone()));
961
962        mock_concurrent_shared_data
963            .expect_insert()
964            .times(0)
965            .in_sequence(&mut sequence)
966            .returning(move |_x, value: InternalData, _handle_consistency| {
967                (TransactionStatus::Complete, value)
968            });
969
970        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
971        cache.delete("key1");
972    }
973
974    #[test]
975    fn remove_from_cache_when_item_is_deleted_but_it_is_already_removed_from_state() {
976        let stored_fifo = VecDeque::from([
977            String::from("test-cache-keyA"),
978            String::from("test-cache-keyB"),
979        ]);
980        let stored_internal_data = InternalData::new(3, stored_fifo, "test-cache-keyB");
981
982        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
983        let mut sequence = Sequence::new();
984
985        mock_concurrent_shared_data
986            .expect_remove()
987            .times(1)
988            .with(eq("test-cache-key1"))
989            .in_sequence(&mut sequence)
990            .returning(move |_key| Some(Vec::<u8>::new()));
991
992        mock_concurrent_shared_data
993            .expect_get::<InternalData>()
994            .with(eq("test-cache-internal_data"))
995            .times(1)
996            .in_sequence(&mut sequence)
997            .returning(move |_x: &str| Some(stored_internal_data.clone()));
998
999        mock_concurrent_shared_data
1000            .expect_insert()
1001            .times(0)
1002            .in_sequence(&mut sequence)
1003            .returning(move |_x, value: InternalData, _handle_consistency| {
1004                (TransactionStatus::Complete, value)
1005            });
1006
1007        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
1008        cache.delete("key1");
1009    }
1010
1011    // TODO: W-10712428 Update test when implement rollback for errors when save internal data.
1012    #[test]
1013    fn remove_from_cache_when_shared_data_fails_updating_state() {
1014        let stored_fifo = VecDeque::from([String::from("test-cache-key1")]);
1015        let stored_internal_data = InternalData::new(3, stored_fifo, "test-cache-key1");
1016
1017        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
1018        let mut sequence = Sequence::new();
1019
1020        mock_concurrent_shared_data
1021            .expect_remove()
1022            .times(1)
1023            .with(eq("test-cache-key1"))
1024            .in_sequence(&mut sequence)
1025            .returning(move |_key| Some(Vec::<u8>::new()));
1026
1027        mock_concurrent_shared_data
1028            .expect_get::<InternalData>()
1029            .with(eq("test-cache-internal_data"))
1030            .times(1)
1031            .in_sequence(&mut sequence)
1032            .returning(move |_x: &str| Some(stored_internal_data.clone()));
1033
1034        mock_concurrent_shared_data
1035            .expect_insert()
1036            .times(1)
1037            .in_sequence(&mut sequence)
1038            .returning(move |_x, value: InternalData, _handle_consistency| {
1039                (TransactionStatus::Rejected, value)
1040            });
1041
1042        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
1043        cache.delete("key1");
1044    }
1045
1046    #[test]
1047    fn get_item_from_cache_ok() {
1048        let obj = SerializableObject {
1049            property_one: 1,
1050            property_two: 10,
1051        };
1052        let serialized_obj = bincode::serialize(&obj).unwrap();
1053
1054        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
1055
1056        mock_concurrent_shared_data
1057            .expect_get::<Vec<u8>>()
1058            .with(eq("test-cache-key1"))
1059            .times(1)
1060            .returning(move |_x: &str| Some(serialized_obj.clone()));
1061
1062        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
1063
1064        let value = cache.get("key1");
1065        assert!(value.is_some());
1066        assert_eq!(value.unwrap(), bincode::serialize(&obj).unwrap());
1067    }
1068
1069    #[test]
1070    fn get_item_from_cache_empty() {
1071        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
1072
1073        mock_concurrent_shared_data
1074            .expect_get::<Vec<u8>>()
1075            .with(eq("test-cache-key1"))
1076            .times(1)
1077            .returning(move |_x: &str| None);
1078
1079        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
1080        assert!(cache.get("key1").is_none());
1081    }
1082
1083    #[test]
1084    fn test_purge() {
1085        let stored_fifo = VecDeque::from([
1086            String::from("test-cache-key1"),
1087            String::from("test-cache-key2"),
1088        ]);
1089        let stored_internal_data = InternalData::new(3, stored_fifo, "test-cache-key2");
1090
1091        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
1092        let mut sequence = Sequence::new();
1093
1094        mock_concurrent_shared_data
1095            .expect_get::<InternalData>()
1096            .with(eq("test-cache-internal_data"))
1097            .times(1)
1098            .in_sequence(&mut sequence)
1099            .returning(move |_x: &str| Some(stored_internal_data.clone()));
1100
1101        mock_concurrent_shared_data
1102            .expect_remove()
1103            .times(1)
1104            .with(eq("test-cache-key1"))
1105            .in_sequence(&mut sequence)
1106            .returning(move |_key| Some(Vec::<u8>::new()));
1107
1108        mock_concurrent_shared_data
1109            .expect_remove()
1110            .times(1)
1111            .with(eq("test-cache-key2"))
1112            .in_sequence(&mut sequence)
1113            .returning(move |_key| Some(Vec::<u8>::new()));
1114
1115        mock_concurrent_shared_data
1116            .expect_remove()
1117            .times(1)
1118            .with(eq("test-cache-internal_data"))
1119            .in_sequence(&mut sequence)
1120            .returning(move |_key| Some(InternalData::new(3, VecDeque::new(), "test-cache-key2")));
1121
1122        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
1123        cache.purge();
1124    }
1125
1126    #[test]
1127    fn test_purge_cache_empty() {
1128        let mut mock_concurrent_shared_data = MockConcurrentSharedData::default();
1129        let mut sequence = Sequence::new();
1130
1131        mock_concurrent_shared_data
1132            .expect_get::<InternalData>()
1133            .with(eq("test-cache-internal_data"))
1134            .times(1)
1135            .in_sequence(&mut sequence)
1136            .returning(move |_x: &str| None);
1137
1138        mock_concurrent_shared_data
1139            .expect_remove::<Vec<u8>>()
1140            .times(0)
1141            .in_sequence(&mut sequence);
1142
1143        mock_concurrent_shared_data
1144            .expect_remove::<InternalData>()
1145            .times(0)
1146            .in_sequence(&mut sequence);
1147
1148        let cache = FifoCache::new(String::from("test-cache"), 3, mock_concurrent_shared_data);
1149        cache.purge();
1150    }
1151}