notedeck

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

commit 09cd8ff379c9f8924613d4fa05db1acfb50a4eca
parent dd7093288b9c4d72d0bba40ff8e0f92b7d970e31
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 24 Dec 2023 12:25:35 -0800

pool: implement keepalive pinging

To prevent us from disconnecting, introduce keepalive pinging. In the
event loop we check if any relays need a refresh ping.

Diffstat:
Menostr/src/relay/pool.rs | 54+++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/app.rs | 8+++++++-
2 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/enostr/src/relay/pool.rs b/enostr/src/relay/pool.rs @@ -2,6 +2,8 @@ use crate::relay::message::RelayEvent; use crate::relay::Relay; use crate::{ClientMessage, Result}; +use std::time::{Duration, Instant}; + #[cfg(not(target_arch = "wasm32"))] use ewebsock::WsMessage; @@ -16,25 +18,42 @@ pub struct PoolEvent<'a> { pub event: RelayEvent, } -pub struct RelayPool { - pub relays: Vec<Relay>, +pub struct PoolRelay { + pub relay: Relay, + pub last_ping: Instant, } -impl Default for RelayPool { - fn default() -> RelayPool { - RelayPool { relays: Vec::new() } +impl PoolRelay { + pub fn new(relay: Relay) -> PoolRelay { + PoolRelay { + relay: relay, + last_ping: Instant::now(), + } } } +pub struct RelayPool { + pub relays: Vec<PoolRelay>, + pub ping_rate: Duration, +} + impl RelayPool { // Constructs a new, empty RelayPool. pub fn new() -> RelayPool { - RelayPool { relays: vec![] } + RelayPool { + relays: vec![], + ping_rate: Duration::from_secs(25), + } + } + + pub fn ping_rate(&mut self, duration: Duration) -> &mut Self { + self.ping_rate = duration; + self } pub fn has(&self, url: &str) -> bool { for relay in &self.relays { - if &relay.url == url { + if &relay.relay.url == url { return true; } } @@ -43,12 +62,27 @@ impl RelayPool { pub fn send(&mut self, cmd: &ClientMessage) { for relay in &mut self.relays { - relay.send(cmd); + relay.relay.send(cmd); + } + } + + /// 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) { + 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(); + } } } pub fn send_to(&mut self, cmd: &ClientMessage, relay_url: &str) { for relay in &mut self.relays { + let relay = &mut relay.relay; if relay.url == relay_url { relay.send(cmd); return; @@ -63,8 +97,9 @@ impl RelayPool { wakeup: impl Fn() + Send + Sync + 'static, ) -> Result<()> { let relay = Relay::new(url, wakeup)?; + let pool_relay = PoolRelay::new(relay); - self.relays.push(relay); + self.relays.push(pool_relay); Ok(()) } @@ -72,6 +107,7 @@ impl RelayPool { /// Attempts to receive a pool event from a list of relays. The function searches each relay in the list in order, attempting to receive a message from each. If a message is received, return it. If no message is received from any relays, None is returned. pub fn try_recv(&mut self) -> Option<PoolEvent<'_>> { for relay in &mut self.relays { + let relay = &mut relay.relay; if let Some(msg) = relay.receiver.try_recv() { match msg.try_into() { Ok(event) => { diff --git a/src/app.rs b/src/app.rs @@ -13,6 +13,7 @@ use enostr::{ClientMessage, EventId, Filter, Profile, Pubkey, RelayEvent, RelayM use poll_promise::Promise; use std::collections::{HashMap, HashSet}; use std::hash::{Hash, Hasher}; +use std::time::Duration; use tracing::{debug, error, info, warn}; use enostr::{Event, RelayPool}; @@ -62,7 +63,7 @@ impl Default for Damus { state: DamusState::Initializing, contacts: Contacts::new(), all_events: HashMap::new(), - pool: RelayPool::default(), + pool: RelayPool::new(), events: vec![], img_cache: HashMap::new(), n_panels: 1, @@ -99,6 +100,7 @@ fn send_initial_filters(pool: &mut RelayPool, relay_url: &str) { let subid = "initial"; for relay in &mut pool.relays { + let relay = &mut relay.relay; if relay.url == relay_url { relay.subscribe(subid.to_string(), vec![filter]); return; @@ -114,6 +116,8 @@ fn try_process_event(damus: &mut Damus, ctx: &egui::Context) { ctx.set_pixels_per_point(ctx.pixels_per_point() - amount); } + damus.pool.keepalive_ping(); + // pool stuff if let Some(ev) = damus.pool.try_recv() { let relay = ev.relay.to_owned(); @@ -251,6 +255,8 @@ fn render_damus(damus: &mut Damus, ctx: &Context) { render_damus_desktop(ctx, damus); } + ctx.request_repaint_after(Duration::from_secs(1)); + #[cfg(feature = "profiling")] puffin_egui::profiler_window(ctx); }