notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit f46797ea10763a9878f1bcc08f85cd541070a69c
parent 6dee4bbe7de5c71dd62946102d5545a4a02ee7d6
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 24 Dec 2023 14:23:29 -0800

net: geometric backoff connection retries on disconnects

Diffstat:
Menostr/src/relay/mod.rs | 8++++++++
Menostr/src/relay/pool.rs | 57++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/app.rs | 6+++++-
3 files changed, 61 insertions(+), 10 deletions(-)

diff --git a/enostr/src/relay/mod.rs b/enostr/src/relay/mod.rs @@ -63,6 +63,14 @@ impl Relay { self.sender.send(txt); } + pub fn connect(&mut self, wakeup: impl Fn() + Send + Sync + 'static) -> Result<()> { + let (sender, receiver) = ewebsock::connect_with_wakeup(&self.url, wakeup)?; + self.status = RelayStatus::Connecting; + self.sender = sender; + self.receiver = receiver; + Ok(()) + } + pub fn ping(&mut self) { let msg = WsMessage::Ping(vec![]); self.sender.send(msg); diff --git a/enostr/src/relay/pool.rs b/enostr/src/relay/pool.rs @@ -1,5 +1,5 @@ use crate::relay::message::RelayEvent; -use crate::relay::Relay; +use crate::relay::{Relay, RelayStatus}; use crate::{ClientMessage, Result}; use std::time::{Duration, Instant}; @@ -21,6 +21,8 @@ pub struct PoolEvent<'a> { pub struct PoolRelay { pub relay: Relay, pub last_ping: Instant, + pub last_connect_attempt: Instant, + pub retry_connect_after: Duration, } impl PoolRelay { @@ -28,8 +30,14 @@ impl PoolRelay { PoolRelay { relay: relay, last_ping: Instant::now(), + last_connect_attempt: Instant::now(), + retry_connect_after: Self::initial_reconnect_duration(), } } + + pub fn initial_reconnect_duration() -> Duration { + Duration::from_secs(2) + } } pub struct RelayPool { @@ -68,14 +76,43 @@ impl RelayPool { /// Keep relay connectiongs alive by pinging relays that haven't been /// pinged in awhile. Adjust ping rate with [`ping_rate`]. - pub fn keepalive_ping(&mut self) { + pub fn keepalive_ping(&mut self, wakeup: impl Fn() + Send + Sync + Clone + 'static) { for relay in &mut self.relays { let now = std::time::Instant::now(); - let should_ping = now - relay.last_ping > self.ping_rate; - if should_ping { - debug!("pinging {}", relay.relay.url); - relay.relay.ping(); - relay.last_ping = Instant::now(); + + match relay.relay.status { + RelayStatus::Disconnected => { + let reconnect_at = relay.last_connect_attempt + relay.retry_connect_after; + if now > reconnect_at { + relay.last_connect_attempt = now; + let next_duration = Duration::from_millis( + ((relay.retry_connect_after.as_millis() as f64) * 1.5) as u64, + ); + debug!( + "bumping reconnect duration from {:?} to {:?} and retrying connect", + relay.retry_connect_after, next_duration + ); + relay.retry_connect_after = next_duration; + relay.relay.connect(wakeup.clone()); + } else { + // let's wait a bit before we try again + } + } + + RelayStatus::Connected => { + relay.retry_connect_after = PoolRelay::initial_reconnect_duration(); + + let should_ping = now - relay.last_ping > self.ping_rate; + if should_ping { + debug!("pinging {}", relay.relay.url); + relay.relay.ping(); + relay.last_ping = Instant::now(); + } + } + + RelayStatus::Connecting => { + // cool story bro + } } } } @@ -94,7 +131,7 @@ impl RelayPool { pub fn add_url( &mut self, url: String, - wakeup: impl Fn() + Send + Sync + 'static, + wakeup: impl Fn() + Send + Sync + Clone + 'static, ) -> Result<()> { let relay = Relay::new(url, wakeup)?; let pool_relay = PoolRelay::new(relay); @@ -111,6 +148,7 @@ impl RelayPool { if let Some(msg) = relay.receiver.try_recv() { match msg.try_into() { Ok(event) => { + relay.status = RelayStatus::Connected; // let's just handle pongs here. // We only need to do this natively. #[cfg(not(target_arch = "wasm32"))] @@ -129,7 +167,8 @@ impl RelayPool { } Err(e) => { - error!("{:?}", e); + relay.status = RelayStatus::Disconnected; + error!("try_recv {:?}", e); continue; } } diff --git a/src/app.rs b/src/app.rs @@ -115,7 +115,11 @@ fn try_process_event(damus: &mut Damus, ctx: &egui::Context) { ctx.set_pixels_per_point(ctx.pixels_per_point() - amount); } - damus.pool.keepalive_ping(); + let ctx2 = ctx.clone(); + let wakeup = move || { + ctx2.request_repaint(); + }; + damus.pool.keepalive_ping(wakeup); // pool stuff while let Some(ev) = damus.pool.try_recv() {