aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-04 05:12:48 +0100
committertv <tv@krebsco.de>2026-11-04 05:12:48 +0100
commite8d1cbddcaec18f79f54cad671f15bbaa189673b (patch)
tree6abf38afd9e03e23267614274d24de221dba4e02 /src
parentd8395ad72994bca696f6de6e511f49673a93b260 (diff)
unanswered sync asked after
An overdue sync answer is now followed by a PING too, and the peer stays reached once it answers the PING.
Diffstat (limited to 'src')
-rw-r--r--src/state/peer.rs34
1 files changed, 28 insertions, 6 deletions
diff --git a/src/state/peer.rs b/src/state/peer.rs
index a1d40ab..434f5fd 100644
--- a/src/state/peer.rs
+++ b/src/state/peer.rs
@@ -110,10 +110,9 @@ pub(crate) struct PeerState {
pub(crate) last_status_at: Option<Instant>,
/// Since when we wait for an answer from it (to a keepalive, or to a sync message
/// whose answer is overdue) with nothing heard from it since, and whether we asked
- /// again: halfway to `Timing::unreached_after` a PING (`take_ask_again_due`; a sync
- /// message whose answer is overdue goes again anyway), so one lost message or answer
- /// doesn't do it; after it, we don't count it as reached (`swarm::reach`). Cleared by
- /// any message from it (`mark_seen`).
+ /// again: halfway to `Timing::unreached_after` a PING (`take_ask_again_due`), so one
+ /// lost message or answer doesn't do it; after it, we don't count it as reached
+ /// (`swarm::reach`). Cleared by any message from it (`mark_seen`).
awaiting_answer: Option<(Instant, bool)>,
/// Last time a sync message from this peer was applied (survives resets: the
@@ -283,8 +282,9 @@ impl PeerState {
if overdue.is_some() {
self.incidents.reply_timeouts += 1;
if let Some(started) = started {
- // It goes again now: that is asking again.
- self.awaiting_answer.get_or_insert((started, true));
+ // Asked again with a PING too: a peer that has nothing to say answers no
+ // sync message, however often it goes.
+ self.awaiting_answer.get_or_insert((started, false));
}
if let Some(largest) = largest {
self.whole_failed(largest, now);
@@ -753,6 +753,28 @@ mod tests {
assert!(!peer.answer_overdue(&timing, sent + timing.unreached_after * 2));
}
+ /// A sync message whose answer is overdue is asked after with a PING too: a peer with
+ /// nothing to say answers none, however often it goes, and still counts as reached
+ /// once it answers the PING.
+ #[test]
+ fn unanswered_sync_messages_are_asked_after() {
+ let timing = Timing::from_keepalive(Duration::from_secs(60), Duration::from_secs(300));
+ let mut peer = PeerState::new(true, &timing);
+ peer.hello_state = HelloState::Established;
+ let sent = peer.added_at;
+ peer.sync.expect_reply(
+ Vec::new(),
+ sent,
+ crate::swarm::throughput::SyncBytes::default(),
+ 100,
+ );
+ let overdue = sent + timing.sync_reply_timeout;
+ assert!(peer.take_overdue_sync_reply(overdue).is_some());
+ assert!(peer.take_ask_again_due(&timing, overdue));
+ peer.mark_seen(overdue + Duration::from_millis(50));
+ assert!(!peer.answer_overdue(&timing, sent + timing.unreached_after));
+ }
+
/// Keepalives are PINGs, every `status_refresh` a STATUS; only when quiet.
#[test]
fn keepalives_are_pings_and_now_and_then_a_status() {