upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-07-15 15:03:11 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2025-07-15 15:03:33 +0100
commit8cea674f58e806c22d2887cff5e6f76bc4dba0db (patch)
treec912c086c3d9b6598e68bb5d66ecf928d28ad940
parent8d42d8c3e8e65ff1892effa2d1058d9f2422ce2b (diff)
feat(client): skip relays that just timeout
Prevent repeated attempts to connect to relays that have previously failed due to connection timeouts within the current session, improving overall performance and reliability. Added a `relays_not_to_retry` map to the `Client` struct to store relays to skip and their reasons. Implemented methods `skip_relay_for_session` and `is_relay_skipped_for_session` to manage this map. Relays are added to the skip list upon connection timeouts. Subsequent attempts to use these skipped relays during the same session will immediately fail with the recorded reason, avoiding unnecessary network calls. Progress bars are updated to reflect skipped relays and their reasons.
-rw-r--r--src/lib/client.rs130
1 files changed, 102 insertions, 28 deletions
diff --git a/src/lib/client.rs b/src/lib/client.rs
index 3b613b6..445bf77 100644
--- a/src/lib/client.rs
+++ b/src/lib/client.rs
@@ -15,11 +15,11 @@ use std::{
15 fmt::{Display, Write}, 15 fmt::{Display, Write},
16 fs::create_dir_all, 16 fs::create_dir_all,
17 path::Path, 17 path::Path,
18 sync::Arc, 18 sync::{Arc, RwLock},
19 time::Duration, 19 time::Duration,
20}; 20};
21 21
22use anyhow::{Context, Result, bail}; 22use anyhow::{Context, Result, anyhow, bail};
23use async_trait::async_trait; 23use async_trait::async_trait;
24use console::Style; 24use console::Style;
25use futures::{ 25use futures::{
@@ -60,6 +60,29 @@ pub struct Client {
60 more_fallback_relays: Vec<String>, 60 more_fallback_relays: Vec<String>,
61 blaster_relays: Vec<String>, 61 blaster_relays: Vec<String>,
62 fallback_signer_relays: Vec<String>, 62 fallback_signer_relays: Vec<String>,
63 relays_not_to_retry: Arc<RwLock<HashMap<RelayUrl, String>>>,
64}
65
66impl Client {
67 /// Marks a relay as skipped for the current session with a given reason.
68 /// This method encapsulates the write lock for the relays_not_to_retry map.
69 fn skip_relay_for_session(&self, relay_url: RelayUrl, reason: String) {
70 self.relays_not_to_retry
71 .write()
72 .unwrap()
73 .insert(relay_url, reason);
74 }
75
76 /// Checks if a relay should be skipped for the current session and returns
77 /// the reason if it is. This method encapsulates the read lock for the
78 /// relays_not_to_retry map.
79 fn is_relay_skipped_for_session(&self, relay_url: &RelayUrl) -> Option<String> {
80 self.relays_not_to_retry
81 .read()
82 .unwrap()
83 .get(relay_url)
84 .cloned()
85 }
63} 86}
64 87
65#[cfg_attr(test, automock)] 88#[cfg_attr(test, automock)]
@@ -127,6 +150,7 @@ impl Connect for Client {
127 more_fallback_relays: opts.more_fallback_relays, 150 more_fallback_relays: opts.more_fallback_relays,
128 blaster_relays: opts.blaster_relays, 151 blaster_relays: opts.blaster_relays,
129 fallback_signer_relays: opts.fallback_signer_relays, 152 fallback_signer_relays: opts.fallback_signer_relays,
153 relays_not_to_retry: Arc::new(RwLock::new(HashMap::new())),
130 } 154 }
131 } 155 }
132 156
@@ -135,6 +159,9 @@ impl Connect for Client {
135 } 159 }
136 160
137 async fn connect(&self, relay_url: &RelayUrl) -> Result<()> { 161 async fn connect(&self, relay_url: &RelayUrl) -> Result<()> {
162 if let Some(reason) = self.is_relay_skipped_for_session(relay_url) {
163 bail!("{reason}");
164 }
138 self.client 165 self.client
139 .add_relay(relay_url) 166 .add_relay(relay_url)
140 .await 167 .await
@@ -150,6 +177,7 @@ impl Connect for Client {
150 } 177 }
151 178
152 if !relay.is_connected() { 179 if !relay.is_connected() {
180 self.skip_relay_for_session(relay_url.clone(), "connection timeout".to_string());
153 bail!("connection timeout"); 181 bail!("connection timeout");
154 } 182 }
155 Ok(()) 183 Ok(())
@@ -244,21 +272,37 @@ impl Connect for Client {
244 } else { 272 } else {
245 None 273 None
246 }; 274 };
275 fn update_progress_bar_with_error(
276 relay_url: &RelayUrl,
277 pb: Option<ProgressBar>,
278 error: &anyhow::Error,
279 ) {
280 if let Some(pb) = pb {
281 pb.set_style(pb_after_style(false));
282 pb.set_prefix(format!("{: <11}{}", "error", relay_url));
283 pb.finish_with_message(
284 console::style(
285 error.to_string().replace("relay pool error:", "error:"),
286 )
287 .for_stderr()
288 .red()
289 .to_string(),
290 );
291 }
292 }
293 if let Some(reason) = self.is_relay_skipped_for_session(relay.url()) {
294 update_progress_bar_with_error(relay.url(), pb, &anyhow!("{reason}"));
295 bail!("{reason}");
296 }
247 #[allow(clippy::large_futures)] 297 #[allow(clippy::large_futures)]
248 match get_events_of(relay, filters, &pb).await { 298 match get_events_of(relay, filters, &pb).await {
249 Err(error) => { 299 Err(error) => {
250 if let Some(pb) = pb { 300 // Check error for timeout/connection issues and add to skip list
251 pb.set_style(pb_after_style(false)); 301 if error.to_string().contains("connection timeout") {
252 pb.set_prefix(format!("{: <11}{}", "error", relay.url())); 302 // Simple check, refine as needed
253 pb.finish_with_message( 303 self.skip_relay_for_session(relay.url().clone(), error.to_string());
254 console::style(
255 error.to_string().replace("relay pool error:", "error:"),
256 )
257 .for_stderr()
258 .red()
259 .to_string(),
260 );
261 } 304 }
305 update_progress_bar_with_error(relay.url(), pb, &error);
262 Err(error) 306 Err(error)
263 } 307 }
264 Ok(res) => { 308 Ok(res) => {
@@ -391,25 +435,55 @@ impl Connect for Client {
391 } else { 435 } else {
392 None 436 None
393 }; 437 };
394 438 // do here
439
440 fn update_progress_bar_with_error(
441 relay_column_width: usize,
442 relay_url: &RelayUrl,
443 pb: Option<ProgressBar>,
444 error: &anyhow::Error,
445 ) {
446 if let Some(pb) = pb {
447 pb.set_style(pb_after_style(false));
448 pb.set_prefix(
449 Style::new()
450 .color256(247)
451 .apply_to(format!("{: <relay_column_width$}", &relay_url))
452 .to_string(),
453 );
454 pb.finish_with_message(
455 console::style(
456 error.to_string().replace("relay pool error:", "error:"),
457 )
458 .for_stderr()
459 .red()
460 .to_string(),
461 );
462 }
463 }
464 if let Some(reason) = self.is_relay_skipped_for_session(&relay_url) {
465 update_progress_bar_with_error(
466 relay_column_width,
467 &relay_url,
468 pb,
469 &anyhow!("{reason}"),
470 );
471 bail!("{reason}");
472 }
395 #[allow(clippy::large_futures)] 473 #[allow(clippy::large_futures)]
396 match self.fetch_all_from_relay(git_repo_path, request, &pb).await { 474 match self.fetch_all_from_relay(git_repo_path, request, &pb).await {
397 Err(error) => { 475 Err(error) => {
398 if let Some(pb) = pb { 476 // Check error for timeout/connection issues and add to skip list
399 pb.set_style(pb_after_style(false)); 477 if error.to_string().contains("connection timeout") {
400 pb.set_prefix( 478 // Simple check, refine as needed
401 dim.apply_to(format!("{: <relay_column_width$}", &relay_url)) 479 self.skip_relay_for_session(relay_url.clone(), error.to_string());
402 .to_string(),
403 );
404 pb.finish_with_message(
405 console::style(
406 error.to_string().replace("relay pool error:", "error:"),
407 )
408 .for_stderr()
409 .red()
410 .to_string(),
411 );
412 } 480 }
481 update_progress_bar_with_error(
482 relay_column_width,
483 &relay_url,
484 pb,
485 &error,
486 );
413 Err(error) 487 Err(error)
414 } 488 }
415 Ok(res) => Ok(res), 489 Ok(res) => Ok(res),