File size: 4,988 Bytes
c170de8
 
 
db93c31
76795c4
996ff84
 
 
db93c31
76795c4
519ebe0
76795c4
 
 
c170de8
996ff84
1de52de
996ff84
76795c4
 
 
996ff84
 
76795c4
996ff84
519ebe0
c170de8
 
996ff84
76795c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
996ff84
76795c4
996ff84
 
c170de8
 
996ff84
76795c4
996ff84
 
 
 
 
 
c170de8
 
996ff84
c170de8
 
 
 
519ebe0
996ff84
76795c4
 
519ebe0
 
 
 
 
76795c4
996ff84
 
 
 
db93c31
c170de8
 
996ff84
 
c170de8
 
 
 
 
db93c31
3c7edb8
320f5f4
3c7edb8
db93c31
996ff84
76795c4
 
519ebe0
320f5f4
519ebe0
 
 
76795c4
996ff84
320f5f4
996ff84
 
 
 
 
c170de8
996ff84
 
 
 
c170de8
996ff84
 
 
 
 
db93c31
c170de8
996ff84
519ebe0
 
996ff84
 
 
 
 
519ebe0
996ff84
 
320f5f4
996ff84
 
 
519ebe0
996ff84
c170de8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! This module provides the functionality to cache the aggregated results fetched and aggregated
//! from the upstream search engines in a json format.

use error_stack::Report;
#[cfg(feature = "in_memory_cache")]
use mini_moka::sync::Cache as MokaCache;
use std::time::Duration;
use tokio::sync::Mutex;

use crate::{config::parser::Config, results::aggregation_models::SearchResults};

use super::error::PoolError;
#[cfg(feature = "redis")]
use super::redis_cacher::RedisCache;

/// Different implementations for caching, currently it is possible to cache in-memory or in Redis.
#[derive(Clone)]
pub enum Cache {
    /// Caching is disabled
    Disabled,
    #[cfg(feature = "redis")]
    /// Encapsulates the Redis based cache
    Redis(RedisCache),
    #[cfg(feature = "in_memory_cache")]
    /// Contains the in-memory cache.
    InMemory(MokaCache<String, SearchResults>),
}

impl Cache {
    /// Builds the cache from the given configuration.
    pub async fn build(config: &Config) -> Self {
        #[cfg(feature = "redis")]
        if let Some(url) = &config.redis_url {
            log::info!("Using Redis running at {} for caching", &url);
            return Cache::new(
                RedisCache::new(url, 5)
                    .await
                    .expect("Redis cache configured"),
            );
        }
        #[cfg(feature = "in_memory_cache")]
        if config.in_memory_cache {
            log::info!("Using an in-memory cache");
            return Cache::new_in_memory();
        }
        log::info!("Caching is disabled");
        Cache::Disabled
    }

    /// Creates a new cache, which wraps the given RedisCache.
    #[cfg(feature = "redis")]
    pub fn new(redis_cache: RedisCache) -> Self {
        Cache::Redis(redis_cache)
    }

    /// Creates an in-memory cache
    #[cfg(feature = "in_memory_cache")]
    pub fn new_in_memory() -> Self {
        let cache = MokaCache::builder()
            .max_capacity(1000)
            .time_to_live(Duration::from_secs(60))
            .build();
        Cache::InMemory(cache)
    }

    /// A function which fetches the cached json results as json string.
    ///
    /// # Arguments
    ///
    /// * `url` - It takes an url as a string.
    pub async fn cached_json(&mut self, url: &str) -> Result<SearchResults, Report<PoolError>> {
        match self {
            Cache::Disabled => Err(Report::new(PoolError::MissingValue)),
            #[cfg(feature = "redis")]
            Cache::Redis(redis_cache) => {
                let json = redis_cache.cached_json(url).await?;
                Ok(serde_json::from_str::<SearchResults>(&json)
                    .map_err(|_| PoolError::SerializationError)?)
            }
            #[cfg(feature = "in_memory_cache")]
            Cache::InMemory(in_memory) => match in_memory.get(&url.to_string()) {
                Some(res) => Ok(res),
                None => Err(Report::new(PoolError::MissingValue)),
            },
        }
    }

    /// A function which caches the results by using the `url` as the key and
    /// `json results` as the value and stores it in the cache
    ///
    /// # Arguments
    ///
    /// * `json_results` - It takes the json results string as an argument.
    /// * `url` - It takes the url as a String.
    pub async fn cache_results(
        &mut self,
        search_results: &SearchResults,
        url: &str,
    ) -> Result<(), Report<PoolError>> {
        match self {
            Cache::Disabled => Ok(()),
            #[cfg(feature = "redis")]
            Cache::Redis(redis_cache) => {
                let json = serde_json::to_string(search_results)
                    .map_err(|_| PoolError::SerializationError)?;
                redis_cache.cache_results(&json, url).await
            }
            #[cfg(feature = "in_memory_cache")]
            Cache::InMemory(cache) => {
                cache.insert(url.to_string(), search_results.clone());
                Ok(())
            }
        }
    }
}

/// A structure to efficiently share the cache between threads - as it is protected by a Mutex.
pub struct SharedCache {
    cache: Mutex<Cache>,
}

impl SharedCache {
    /// Creates a new SharedCache from a Cache implementation
    pub fn new(cache: Cache) -> Self {
        Self {
            cache: Mutex::new(cache),
        }
    }

    /// A function which retrieves the cached SearchResulsts from the internal cache.
    pub async fn cached_json(&self, url: &str) -> Result<SearchResults, Report<PoolError>> {
        let mut mut_cache = self.cache.lock().await;
        mut_cache.cached_json(url).await
    }

    /// A function which caches the results by using the `url` as the key and
    /// `SearchResults` as the value.
    pub async fn cache_results(
        &self,
        search_results: &SearchResults,
        url: &str,
    ) -> Result<(), Report<PoolError>> {
        let mut mut_cache = self.cache.lock().await;
        mut_cache.cache_results(search_results, url).await
    }
}