alamin655 commited on
Commit
948d20d
·
unverified ·
2 Parent(s): 3a1ff0f 4315221

Merge pull request #599 from neon-mmd/FIX/592_redis-does-not-invalidate-cached-results

Browse files
Cargo.lock CHANGED
@@ -851,7 +851,7 @@ dependencies = [
851
  "clap",
852
  "criterion-plot",
853
  "is-terminal",
854
- "itertools",
855
  "num-traits",
856
  "once_cell",
857
  "oorandom",
@@ -870,7 +870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
870
  checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
871
  dependencies = [
872
  "cast",
873
- "itertools",
874
  ]
875
 
876
  [[package]]
@@ -1881,6 +1881,15 @@ dependencies = [
1881
  "either",
1882
  ]
1883
 
 
 
 
 
 
 
 
 
 
1884
  [[package]]
1885
  name = "itoa"
1886
  version = "0.4.8"
@@ -1983,7 +1992,7 @@ dependencies = [
1983
  "cssparser-color",
1984
  "data-encoding",
1985
  "getrandom",
1986
- "itertools",
1987
  "lazy_static",
1988
  "parcel_selectors",
1989
  "paste",
@@ -4440,7 +4449,7 @@ dependencies = [
4440
 
4441
  [[package]]
4442
  name = "websurfx"
4443
- version = "1.17.0"
4444
  dependencies = [
4445
  "actix-cors",
4446
  "actix-files",
@@ -4460,6 +4469,7 @@ dependencies = [
4460
  "error-stack",
4461
  "fake-useragent",
4462
  "futures 0.3.30",
 
4463
  "keyword_extraction",
4464
  "lightningcss",
4465
  "log",
 
851
  "clap",
852
  "criterion-plot",
853
  "is-terminal",
854
+ "itertools 0.10.5",
855
  "num-traits",
856
  "once_cell",
857
  "oorandom",
 
870
  checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
871
  dependencies = [
872
  "cast",
873
+ "itertools 0.10.5",
874
  ]
875
 
876
  [[package]]
 
1881
  "either",
1882
  ]
1883
 
1884
+ [[package]]
1885
+ name = "itertools"
1886
+ version = "0.13.0"
1887
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1888
+ checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
1889
+ dependencies = [
1890
+ "either",
1891
+ ]
1892
+
1893
  [[package]]
1894
  name = "itoa"
1895
  version = "0.4.8"
 
1992
  "cssparser-color",
1993
  "data-encoding",
1994
  "getrandom",
1995
+ "itertools 0.10.5",
1996
  "lazy_static",
1997
  "parcel_selectors",
1998
  "paste",
 
4449
 
4450
  [[package]]
4451
  name = "websurfx"
4452
+ version = "1.17.20"
4453
  dependencies = [
4454
  "actix-cors",
4455
  "actix-files",
 
4469
  "error-stack",
4470
  "fake-useragent",
4471
  "futures 0.3.30",
4472
+ "itertools 0.13.0",
4473
  "keyword_extraction",
4474
  "lightningcss",
4475
  "log",
Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
  [package]
2
  name = "websurfx"
3
- version = "1.17.0"
4
  edition = "2021"
5
  description = "An open-source alternative to Searx that provides clean, ad-free, and organic results with incredible speed while keeping privacy and security in mind."
6
  repository = "https://github.com/neon-mmd/websurfx"
@@ -82,14 +82,13 @@ base64 = { version = "0.21.5", default-features = false, features = [
82
  cfg-if = { version = "1.0.0", default-features = false, optional = true }
83
  keyword_extraction = { version = "1.4.3", default-features = false, features = [
84
  "tf_idf",
85
-
86
-
87
  ] }
88
 
89
  stop-words = { version = "0.8.0", default-features = false, features = ["iso"] }
90
  thesaurus = { version = "0.5.2", default-features = false, optional = true, features = [
91
  "moby",
92
- ] }
 
93
 
94
  [dev-dependencies]
95
  rusty-hook = { version = "^0.11.2", default-features = false }
 
1
  [package]
2
  name = "websurfx"
3
+ version = "1.17.20"
4
  edition = "2021"
5
  description = "An open-source alternative to Searx that provides clean, ad-free, and organic results with incredible speed while keeping privacy and security in mind."
6
  repository = "https://github.com/neon-mmd/websurfx"
 
82
  cfg-if = { version = "1.0.0", default-features = false, optional = true }
83
  keyword_extraction = { version = "1.4.3", default-features = false, features = [
84
  "tf_idf",
 
 
85
  ] }
86
 
87
  stop-words = { version = "0.8.0", default-features = false, features = ["iso"] }
88
  thesaurus = { version = "0.5.2", default-features = false, optional = true, features = [
89
  "moby",
90
+ ]}
91
+ itertools = {version = "0.13.0", default-features = false}
92
 
93
  [dev-dependencies]
94
  rusty-hook = { version = "^0.11.2", default-features = false }
src/cache/redis_cacher.rs CHANGED
@@ -4,7 +4,10 @@
4
  use super::error::CacheError;
5
  use error_stack::Report;
6
  use futures::stream::FuturesUnordered;
7
- use redis::{aio::ConnectionManager, AsyncCommands, Client, RedisError};
 
 
 
8
 
9
  /// A constant holding the redis pipeline size.
10
  const REDIS_PIPELINE_SIZE: usize = 3;
@@ -139,8 +142,14 @@ impl RedisCache {
139
  self.current_connection = Default::default();
140
 
141
  for (key, json_result) in keys.zip(json_results) {
142
- self.pipeline
143
- .set_ex(key, json_result, self.cache_ttl.into());
 
 
 
 
 
 
144
  }
145
 
146
  let mut result: Result<(), RedisError> = self
 
4
  use super::error::CacheError;
5
  use error_stack::Report;
6
  use futures::stream::FuturesUnordered;
7
+ use redis::{
8
+ aio::ConnectionManager, AsyncCommands, Client, ExistenceCheck, RedisError, SetExpiry,
9
+ SetOptions,
10
+ };
11
 
12
  /// A constant holding the redis pipeline size.
13
  const REDIS_PIPELINE_SIZE: usize = 3;
 
142
  self.current_connection = Default::default();
143
 
144
  for (key, json_result) in keys.zip(json_results) {
145
+ self.pipeline.set_options(
146
+ key,
147
+ json_result,
148
+ SetOptions::default()
149
+ .conditional_set(ExistenceCheck::NX)
150
+ .get(true)
151
+ .with_expiration(SetExpiry::EX(self.cache_ttl.into())),
152
+ );
153
  }
154
 
155
  let mut result: Result<(), RedisError> = self
src/server/routes/search.rs CHANGED
@@ -12,6 +12,7 @@ use crate::{
12
  results::aggregator::aggregate,
13
  };
14
  use actix_web::{get, http::header::ContentType, web, HttpRequest, HttpResponse};
 
15
  use regex::Regex;
16
  use std::borrow::Cow;
17
  use tokio::{
@@ -40,7 +41,6 @@ pub async fn search(
40
  config: web::Data<&'static Config>,
41
  cache: web::Data<&'static SharedCache>,
42
  ) -> Result<HttpResponse, Box<dyn std::error::Error>> {
43
- use std::sync::Arc;
44
  let params = web::Query::<SearchParams>::from_query(req.query_string())?;
45
  match &params.q {
46
  Some(query) => {
@@ -83,44 +83,36 @@ pub async fn search(
83
  let previous_page = page.saturating_sub(1);
84
  let next_page = page + 1;
85
 
86
- let mut results = Arc::new((SearchResults::default(), String::default()));
87
  if page != previous_page {
88
  let (previous_results, current_results, next_results) = join!(
89
  get_results(previous_page),
90
  get_results(page),
91
  get_results(next_page)
92
  );
93
- let (parsed_previous_results, parsed_next_results) =
94
- (previous_results?, next_results?);
95
 
96
- let (cache_keys, results_list) = (
97
- [
98
- parsed_previous_results.1,
99
- results.1.clone(),
100
- parsed_next_results.1,
101
- ],
102
- [
103
- parsed_previous_results.0,
104
- results.0.clone(),
105
- parsed_next_results.0,
106
- ],
107
- );
108
 
109
- results = Arc::new(current_results?);
 
 
 
 
 
 
110
 
111
  tokio::spawn(async move { cache.cache_results(&results_list, &cache_keys).await });
112
  } else {
113
  let (current_results, next_results) =
114
  join!(get_results(page), get_results(page + 1));
115
 
116
- let parsed_next_results = next_results?;
117
-
118
- results = Arc::new(current_results?);
119
 
120
- let (cache_keys, results_list) = (
121
- [results.1.clone(), parsed_next_results.1.clone()],
122
- [results.0.clone(), parsed_next_results.0],
123
- );
 
124
 
125
  tokio::spawn(async move { cache.cache_results(&results_list, &cache_keys).await });
126
  }
@@ -163,7 +155,7 @@ async fn results(
163
  query: &str,
164
  page: u32,
165
  search_settings: &server_models::Cookie<'_>,
166
- ) -> Result<(SearchResults, String), Box<dyn std::error::Error>> {
167
  // eagerly parse cookie value to evaluate safe search level
168
  let safe_search_level = search_settings.safe_search_level;
169
 
@@ -182,7 +174,7 @@ async fn results(
182
  // check if fetched cache results was indeed fetched or it was an error and if so
183
  // handle the data accordingly.
184
  match cached_results {
185
- Ok(results) => Ok((results, cache_key)),
186
  Err(_) => {
187
  if safe_search_level == 4 {
188
  let mut results: SearchResults = SearchResults::default();
@@ -196,7 +188,7 @@ async fn results(
196
  .cache_results(&[results.clone()], &[cache_key.clone()])
197
  .await?;
198
  results.set_safe_search_level(safe_search_level);
199
- return Ok((results, cache_key));
200
  }
201
  }
202
 
@@ -235,7 +227,7 @@ async fn results(
235
  .cache_results(&[results.clone()], &[cache_key.clone()])
236
  .await?;
237
  results.set_safe_search_level(safe_search_level);
238
- Ok((results, cache_key))
239
  }
240
  }
241
  }
 
12
  results::aggregator::aggregate,
13
  };
14
  use actix_web::{get, http::header::ContentType, web, HttpRequest, HttpResponse};
15
+ use itertools::Itertools;
16
  use regex::Regex;
17
  use std::borrow::Cow;
18
  use tokio::{
 
41
  config: web::Data<&'static Config>,
42
  cache: web::Data<&'static SharedCache>,
43
  ) -> Result<HttpResponse, Box<dyn std::error::Error>> {
 
44
  let params = web::Query::<SearchParams>::from_query(req.query_string())?;
45
  match &params.q {
46
  Some(query) => {
 
83
  let previous_page = page.saturating_sub(1);
84
  let next_page = page + 1;
85
 
86
+ let results: (SearchResults, String, bool);
87
  if page != previous_page {
88
  let (previous_results, current_results, next_results) = join!(
89
  get_results(previous_page),
90
  get_results(page),
91
  get_results(next_page)
92
  );
 
 
93
 
94
+ results = current_results?;
 
 
 
 
 
 
 
 
 
 
 
95
 
96
+ let (results_list, cache_keys): (Vec<SearchResults>, Vec<String>) =
97
+ [previous_results?, results.clone(), next_results?]
98
+ .into_iter()
99
+ .filter_map(|(result, cache_key, flag)| {
100
+ dbg!(flag).then_some((result, cache_key))
101
+ })
102
+ .multiunzip();
103
 
104
  tokio::spawn(async move { cache.cache_results(&results_list, &cache_keys).await });
105
  } else {
106
  let (current_results, next_results) =
107
  join!(get_results(page), get_results(page + 1));
108
 
109
+ results = current_results?;
 
 
110
 
111
+ let (results_list, cache_keys): (Vec<SearchResults>, Vec<String>) =
112
+ [results.clone(), next_results?]
113
+ .into_iter()
114
+ .filter_map(|(result, cache_key, flag)| flag.then_some((result, cache_key)))
115
+ .multiunzip();
116
 
117
  tokio::spawn(async move { cache.cache_results(&results_list, &cache_keys).await });
118
  }
 
155
  query: &str,
156
  page: u32,
157
  search_settings: &server_models::Cookie<'_>,
158
+ ) -> Result<(SearchResults, String, bool), Box<dyn std::error::Error>> {
159
  // eagerly parse cookie value to evaluate safe search level
160
  let safe_search_level = search_settings.safe_search_level;
161
 
 
174
  // check if fetched cache results was indeed fetched or it was an error and if so
175
  // handle the data accordingly.
176
  match cached_results {
177
+ Ok(results) => Ok((results, cache_key, false)),
178
  Err(_) => {
179
  if safe_search_level == 4 {
180
  let mut results: SearchResults = SearchResults::default();
 
188
  .cache_results(&[results.clone()], &[cache_key.clone()])
189
  .await?;
190
  results.set_safe_search_level(safe_search_level);
191
+ return Ok((results, cache_key, true));
192
  }
193
  }
194
 
 
227
  .cache_results(&[results.clone()], &[cache_key.clone()])
228
  .await?;
229
  results.set_safe_search_level(safe_search_level);
230
+ Ok((results, cache_key, true))
231
  }
232
  }
233
  }