neon_arch commited on
Commit
03384d4
โ€ข
1 Parent(s): 12a7eeb

๐Ÿ› ๏ธ fix: implement `hybrid` caching and improve documentation (#244)

Browse files
Files changed (1) hide show
  1. src/cache/cacher.rs +147 -33
src/cache/cacher.rs CHANGED
@@ -19,46 +19,78 @@ use super::redis_cacher::RedisCache;
19
  pub enum Cache {
20
  /// Caching is disabled
21
  Disabled,
22
- #[cfg(feature = "redis-cache")]
23
  /// Encapsulates the Redis based cache
24
  Redis(RedisCache),
25
- #[cfg(feature = "memory-cache")]
26
  /// Contains the in-memory cache.
27
  InMemory(MokaCache<String, SearchResults>),
 
 
 
28
  }
29
 
30
  impl Cache {
31
- /// Builds the cache from the given configuration.
 
 
 
 
 
 
 
 
32
  pub async fn build(_config: &Config) -> Self {
33
- #[cfg(feature = "redis-cache")]
34
- if let Some(url) = &_config.redis_url {
35
- log::info!("Using Redis running at {} for caching", &url);
36
- return Cache::new(
37
- RedisCache::new(url, 5)
38
- .await
39
- .expect("Redis cache configured"),
40
- );
41
- }
42
- #[cfg(feature = "memory-cache")]
 
 
 
 
 
 
 
43
  {
44
  log::info!("Using an in-memory cache");
45
  return Cache::new_in_memory();
46
  }
47
- #[cfg(not(feature = "memory-cache"))]
48
  {
49
  log::info!("Caching is disabled");
50
  Cache::Disabled
51
  }
52
  }
53
 
54
- /// Creates a new cache, which wraps the given RedisCache.
55
- #[cfg(feature = "redis-cache")]
 
 
 
 
 
 
 
 
56
  pub fn new(redis_cache: RedisCache) -> Self {
57
  Cache::Redis(redis_cache)
58
  }
59
 
60
- /// Creates an in-memory cache
61
- #[cfg(feature = "memory-cache")]
 
 
 
 
 
 
62
  pub fn new_in_memory() -> Self {
63
  let cache = MokaCache::builder()
64
  .max_capacity(1000)
@@ -67,25 +99,61 @@ impl Cache {
67
  Cache::InMemory(cache)
68
  }
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  /// A function which fetches the cached json results as json string.
71
  ///
72
  /// # Arguments
73
  ///
74
  /// * `url` - It takes an url as a string.
75
- pub async fn cached_json(&mut self, url: &str) -> Result<SearchResults, Report<PoolError>> {
 
 
 
 
 
76
  match self {
77
  Cache::Disabled => Err(Report::new(PoolError::MissingValue)),
78
- #[cfg(feature = "redis-cache")]
79
  Cache::Redis(redis_cache) => {
80
- let json = redis_cache.cached_json(url).await?;
81
  Ok(serde_json::from_str::<SearchResults>(&json)
82
  .map_err(|_| PoolError::SerializationError)?)
83
  }
84
- #[cfg(feature = "memory-cache")]
85
- Cache::InMemory(in_memory) => match in_memory.get(&url.to_string()) {
86
  Some(res) => Ok(res),
87
  None => Err(Report::new(PoolError::MissingValue)),
88
  },
 
 
 
 
 
 
 
 
 
89
  }
90
  }
91
 
@@ -96,24 +164,42 @@ impl Cache {
96
  ///
97
  /// * `json_results` - It takes the json results string as an argument.
98
  /// * `url` - It takes the url as a String.
 
 
 
 
 
 
99
  pub async fn cache_results(
100
  &mut self,
101
- search_results: &SearchResults,
102
- url: &str,
103
  ) -> Result<(), Report<PoolError>> {
104
  match self {
105
  Cache::Disabled => Ok(()),
106
- #[cfg(feature = "redis-cache")]
107
  Cache::Redis(redis_cache) => {
108
- let json = serde_json::to_string(search_results)
109
  .map_err(|_| PoolError::SerializationError)?;
110
- redis_cache.cache_results(&json, url).await
111
  }
112
- #[cfg(feature = "memory-cache")]
113
  Cache::InMemory(cache) => {
114
- cache.insert(url.to_string(), search_results.clone());
115
  Ok(())
116
  }
 
 
 
 
 
 
 
 
 
 
 
 
117
  }
118
  }
119
  }
@@ -125,21 +211,49 @@ pub struct SharedCache {
125
  }
126
 
127
  impl SharedCache {
128
- /// Creates a new SharedCache from a Cache implementation
 
 
 
 
 
 
129
  pub fn new(cache: Cache) -> Self {
130
  Self {
131
  cache: Mutex::new(cache),
132
  }
133
  }
134
 
135
- /// A function which retrieves the cached SearchResulsts from the internal cache.
 
 
 
 
 
 
 
 
 
 
136
  pub async fn cached_json(&self, url: &str) -> Result<SearchResults, Report<PoolError>> {
137
  let mut mut_cache = self.cache.lock().await;
138
  mut_cache.cached_json(url).await
139
  }
140
 
141
- /// A function which caches the results by using the `url` as the key and
142
  /// `SearchResults` as the value.
 
 
 
 
 
 
 
 
 
 
 
 
143
  pub async fn cache_results(
144
  &self,
145
  search_results: &SearchResults,
 
19
  pub enum Cache {
20
  /// Caching is disabled
21
  Disabled,
22
+ #[cfg(all(feature = "redis-cache", not(feature = "memory-cache")))]
23
  /// Encapsulates the Redis based cache
24
  Redis(RedisCache),
25
+ #[cfg(all(feature = "memory-cache", not(feature = "redis-cache")))]
26
  /// Contains the in-memory cache.
27
  InMemory(MokaCache<String, SearchResults>),
28
+ #[cfg(all(feature = "redis-cache", feature = "memory-cache"))]
29
+ /// Contains both the in-memory cache and Redis based cache
30
+ Hybrid(RedisCache, MokaCache<String, SearchResults>),
31
  }
32
 
33
  impl Cache {
34
+ /// A function that builds the cache from the given configuration.
35
+ ///
36
+ /// # Arguments
37
+ ///
38
+ /// * `config` - It takes the config struct as an argument.
39
+ ///
40
+ /// # Returns
41
+ ///
42
+ /// It returns a newly initialized variant based on the feature enabled by the user.
43
  pub async fn build(_config: &Config) -> Self {
44
+ #[cfg(all(feature = "redis-cache", feature = "memory-cache"))]
45
+ log::info!("Using a hybrid cache");
46
+ #[cfg(all(feature = "redis-cache", feature = "memory-cache"))]
47
+ return Cache::new_hybrid(
48
+ RedisCache::new(&_config.redis_url, 5)
49
+ .await
50
+ .expect("Redis cache configured"),
51
+ );
52
+ #[cfg(all(feature = "redis-cache", not(feature = "memory-cache")))]
53
+ log::info!("Listening redis server on {}", &_config.redis_url);
54
+ #[cfg(all(feature = "redis-cache", not(feature = "memory-cache")))]
55
+ return Cache::new(
56
+ RedisCache::new(&_config.redis_url, 5)
57
+ .await
58
+ .expect("Redis cache configured"),
59
+ );
60
+ #[cfg(all(feature = "memory-cache", not(feature = "redis-cache")))]
61
  {
62
  log::info!("Using an in-memory cache");
63
  return Cache::new_in_memory();
64
  }
65
+ #[cfg(not(any(feature = "memory-cache", feature = "redis-cache")))]
66
  {
67
  log::info!("Caching is disabled");
68
  Cache::Disabled
69
  }
70
  }
71
 
72
+ /// A function that initializes a new connection pool struct.
73
+ ///
74
+ /// # Arguments
75
+ ///
76
+ /// * `redis_cache` - It takes the newly initialized connection pool struct as an argument.
77
+ ///
78
+ /// # Returns
79
+ ///
80
+ /// It returns a `Redis` variant with the newly initialized connection pool struct.
81
+ #[cfg(all(feature = "redis-cache", not(feature = "memory-cache")))]
82
  pub fn new(redis_cache: RedisCache) -> Self {
83
  Cache::Redis(redis_cache)
84
  }
85
 
86
+ /// A function that initializes the `in memory` cache which is used to cache the results in
87
+ /// memory with the search engine thus improving performance by making retrieval and caching of
88
+ /// results faster.
89
+ ///
90
+ /// # Returns
91
+ ///
92
+ /// It returns a `InMemory` variant with the newly initialized in memory cache type.
93
+ #[cfg(all(feature = "memory-cache", not(feature = "redis-cache")))]
94
  pub fn new_in_memory() -> Self {
95
  let cache = MokaCache::builder()
96
  .max_capacity(1000)
 
99
  Cache::InMemory(cache)
100
  }
101
 
102
+ /// A function that initializes both in memory cache and redis client connection for being used
103
+ /// for managing hybrid cache which increases resiliancy of the search engine by allowing the
104
+ /// cache to switch to `in memory` caching if the `redis` cache server is temporarily
105
+ /// unavailable.
106
+ ///
107
+ /// # Arguments
108
+ ///
109
+ /// * `redis_cache` - It takes `redis` client connection struct as an argument.
110
+ ///
111
+ /// # Returns
112
+ ///
113
+ /// It returns a tuple variant `Hybrid` storing both the in-memory cache type and the `redis`
114
+ /// client connection struct.
115
+ #[cfg(all(feature = "redis-cache", feature = "memory-cache"))]
116
+ pub fn new_hybrid(redis_cache: RedisCache) -> Self {
117
+ let cache = MokaCache::builder()
118
+ .max_capacity(1000)
119
+ .time_to_live(Duration::from_secs(60))
120
+ .build();
121
+ Cache::Hybrid(redis_cache, cache)
122
+ }
123
+
124
  /// A function which fetches the cached json results as json string.
125
  ///
126
  /// # Arguments
127
  ///
128
  /// * `url` - It takes an url as a string.
129
+ ///
130
+ /// # Error
131
+ ///
132
+ /// Returns the `SearchResults` from the cache if the program executes normally otherwise
133
+ /// returns a `CacheError` if the results cannot be retrieved from the cache.
134
+ pub async fn cached_json(&mut self, _url: &str) -> Result<SearchResults, Report<PoolError>> {
135
  match self {
136
  Cache::Disabled => Err(Report::new(PoolError::MissingValue)),
137
+ #[cfg(all(feature = "redis-cache", not(feature = "memory-cache")))]
138
  Cache::Redis(redis_cache) => {
139
+ let json = redis_cache.cached_json(_url).await?;
140
  Ok(serde_json::from_str::<SearchResults>(&json)
141
  .map_err(|_| PoolError::SerializationError)?)
142
  }
143
+ #[cfg(all(feature = "memory-cache", not(feature = "redis-cache")))]
144
+ Cache::InMemory(in_memory) => match in_memory.get(&_url.to_string()) {
145
  Some(res) => Ok(res),
146
  None => Err(Report::new(PoolError::MissingValue)),
147
  },
148
+ #[cfg(all(feature = "redis-cache", feature = "memory-cache"))]
149
+ Cache::Hybrid(redis_cache, in_memory) => match redis_cache.cached_json(_url).await {
150
+ Ok(res) => Ok(serde_json::from_str::<SearchResults>(&res)
151
+ .map_err(|_| PoolError::SerializationError)?),
152
+ Err(_) => match in_memory.get(&_url.to_string()) {
153
+ Some(res) => Ok(res),
154
+ None => Err(Report::new(PoolError::MissingValue)),
155
+ },
156
+ },
157
  }
158
  }
159
 
 
164
  ///
165
  /// * `json_results` - It takes the json results string as an argument.
166
  /// * `url` - It takes the url as a String.
167
+ ///
168
+ /// # Error
169
+ ///
170
+ /// Returns a unit type if the program caches the given search results without a failure
171
+ /// otherwise it returns a `CacheError` if the search results cannot be cached due to a
172
+ /// failure.
173
  pub async fn cache_results(
174
  &mut self,
175
+ _search_results: &SearchResults,
176
+ _url: &str,
177
  ) -> Result<(), Report<PoolError>> {
178
  match self {
179
  Cache::Disabled => Ok(()),
180
+ #[cfg(all(feature = "redis-cache", not(feature = "memory-cache")))]
181
  Cache::Redis(redis_cache) => {
182
+ let json = serde_json::to_string(_search_results)
183
  .map_err(|_| PoolError::SerializationError)?;
184
+ redis_cache.cache_results(&json, _url).await
185
  }
186
+ #[cfg(all(feature = "memory-cache", not(feature = "redis-cache")))]
187
  Cache::InMemory(cache) => {
188
+ cache.insert(_url.to_string(), _search_results.clone());
189
  Ok(())
190
  }
191
+ #[cfg(all(feature = "memory-cache", feature = "redis-cache"))]
192
+ Cache::Hybrid(redis_cache, cache) => {
193
+ let json = serde_json::to_string(_search_results)
194
+ .map_err(|_| PoolError::SerializationError)?;
195
+ match redis_cache.cache_results(&json, _url).await {
196
+ Ok(_) => Ok(()),
197
+ Err(_) => {
198
+ cache.insert(_url.to_string(), _search_results.clone());
199
+ Ok(())
200
+ }
201
+ }
202
+ }
203
  }
204
  }
205
  }
 
211
  }
212
 
213
  impl SharedCache {
214
+ /// A function that creates a new `SharedCache` from a Cache implementation.
215
+ ///
216
+ /// # Arguments
217
+ ///
218
+ /// * `cache` - It takes the `Cache` enum variant as an argument with the prefered cache type.
219
+ ///
220
+ /// Returns a newly constructed `SharedCache` struct.
221
  pub fn new(cache: Cache) -> Self {
222
  Self {
223
  cache: Mutex::new(cache),
224
  }
225
  }
226
 
227
+ /// A getter function which retrieves the cached SearchResulsts from the internal cache.
228
+ ///
229
+ /// # Arguments
230
+ ///
231
+ /// * `url` - It takes the search url as an argument which will be used as the key to fetch the
232
+ /// cached results from the cache.
233
+ ///
234
+ /// # Error
235
+ ///
236
+ /// Returns a `SearchResults` struct containing the search results from the cache if nothing
237
+ /// goes wrong otherwise returns a `CacheError`.
238
  pub async fn cached_json(&self, url: &str) -> Result<SearchResults, Report<PoolError>> {
239
  let mut mut_cache = self.cache.lock().await;
240
  mut_cache.cached_json(url).await
241
  }
242
 
243
+ /// A setter function which caches the results by using the `url` as the key and
244
  /// `SearchResults` as the value.
245
+ ///
246
+ /// # Arguments
247
+ ///
248
+ /// * `search_results` - It takes the `SearchResults` as an argument which are results that
249
+ /// needs to be cached.
250
+ /// * `url` - It takes the search url as an argument which will be used as the key for storing
251
+ /// results in the cache.
252
+ ///
253
+ /// # Error
254
+ ///
255
+ /// Returns an unit type if the results are cached succesfully otherwise returns a `CacheError`
256
+ /// on a failure.
257
  pub async fn cache_results(
258
  &self,
259
  search_results: &SearchResults,