summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-26 05:50:14 +0100
committertv <tv@krebsco.de>2026-10-26 05:50:14 +0100
commitf00378e8609e0117132d7aab3f4468a1721f52fa (patch)
tree5a252c09685cf833f5e1a7fc2e2e4e4b3f3c02dd /src
parent51e8c8e38c05d98487784e21f9df54cc18875b8b (diff)
keep silent strangers for a day
Diffstat (limited to 'src')
-rw-r--r--src/swarm/peer/forget.rs40
-rw-r--r--src/swarm/peer/worker.rs3
2 files changed, 40 insertions, 3 deletions
diff --git a/src/swarm/peer/forget.rs b/src/swarm/peer/forget.rs
index 624ca97..df09c3c 100644
--- a/src/swarm/peer/forget.rs
+++ b/src/swarm/peer/forget.rs
@@ -1,5 +1,8 @@
//! Swarm-wide forget records (`_continuum/forgotten/<pk>`): a peer forgotten on one
-//! node is forgotten by all; a forgotten node that is alive cancels its record.
+//! node is forgotten by all; a forgotten node that is alive cancels its record. And
+//! locally, strangers that went silent.
+
+use std::time::Duration;
use automerge::AutoCommit;
use tokio::time::Instant;
@@ -7,7 +10,7 @@ use tracing::{info, warn};
use crate::document::meta;
use crate::document::store::{self, StoreError};
-use crate::state::AppState;
+use crate::state::{AppState, Standing};
fn forgotten_dir() -> Vec<String> {
meta::path(&[meta::FORGOTTEN])
@@ -77,3 +80,36 @@ pub(super) async fn apply_forget_records(state: &AppState) {
})
.await;
}
+
+/// How long a peer that never became a member is kept once nothing more is heard from
+/// it: one listed to us that never answered, or one that keeps failing the handshake
+/// (another swarm, a wrong key). Anyone can name peers to a node, so without a limit
+/// the map of peers would only grow.
+const STRANGER_KEPT: Duration = Duration::from_secs(24 * 3600);
+
+/// Forgets peers that never became members and were silent for `STRANGER_KEPT`. Only
+/// here, not swarm-wide; one that comes back is a stranger again.
+pub(super) async fn forget_strangers(state: &AppState) {
+ let now = Instant::now();
+ let mut strangers = Vec::new();
+ for (peer, peer_state) in state.peers_snapshot().await {
+ let peer_state = peer_state.lock().await;
+ let quiet_since = peer_state.last_seen.unwrap_or(peer_state.added_at);
+ if !matches!(peer_state.standing, Standing::Member { .. })
+ && now.saturating_duration_since(quiet_since) >= STRANGER_KEPT
+ {
+ strangers.push(peer);
+ }
+ }
+ if strangers.is_empty() {
+ return;
+ }
+ let mut peer_states = state.peer_states.write().await;
+ for peer in &strangers {
+ peer_states.remove(peer);
+ }
+ info!(
+ forgotten = strangers.len(),
+ "Forgot peers that never became members and were silent for a day"
+ );
+}
diff --git a/src/swarm/peer/worker.rs b/src/swarm/peer/worker.rs
index a27f144..5161ae6 100644
--- a/src/swarm/peer/worker.rs
+++ b/src/swarm/peer/worker.rs
@@ -8,7 +8,7 @@ use std::time::Duration;
use tokio::time::{Instant, MissedTickBehavior};
use tracing::{debug, info, warn};
-use super::forget::apply_forget_records;
+use super::forget::{apply_forget_records, forget_strangers};
use super::handshake::retry_hello_if_due;
use super::keepalive::send_ping;
use crate::locks::MutexExt;
@@ -94,6 +94,7 @@ pub(crate) async fn run_peer_worker(state: AppState) {
if last_forget_check.is_none_or(|t| t.elapsed() >= FORGET_CHECK_INTERVAL) {
apply_forget_records(&state).await;
+ forget_strangers(&state).await;
crate::swarm::members::ensure_registered(&state).await;
crate::swarm::stability::tick(&state).await;
crate::swarm::stats::measure_saved_size(&state);