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:
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() {